gnunet-svn
[Top][All Lists]
Advanced

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

[taler-donau] branch master updated (939058c -> 9576945)


From: gnunet
Subject: [taler-donau] branch master updated (939058c -> 9576945)
Date: Sun, 14 Jan 2024 22:56:52 +0100

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

pius-loosli pushed a change to branch master
in repository donau.

    from 939058c  Merge branch 'master' of ssh://git.taler.net/donau
     new ade143d  [secmod/helpers] Add helpers from Exchange and adapt to Donau
     new 9576945  [donau] Work on donau /keys

The 2 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/donau/donau-httpd.c         |    7 +-
 src/donau/donau-httpd_keys.c    |  838 +++++++++++++++++--------
 src/donau/donau-httpd_keys.h    |    4 +-
 src/include/donau_crypto_lib.h  |  565 ++++++++++++++++-
 src/util/Makefile.am            |    5 +-
 src/util/crypto_helper_common.c |   51 ++
 src/util/crypto_helper_common.h |   41 ++
 src/util/crypto_helper_cs.c     | 1317 +++++++++++++++++++++++++++++++++++++++
 src/util/crypto_helper_esign.c  |  557 +++++++++++++++++
 src/util/crypto_helper_rsa.c    |  917 +++++++++++++++++++++++++++
 10 files changed, 4033 insertions(+), 269 deletions(-)
 create mode 100644 src/util/crypto_helper_common.c
 create mode 100644 src/util/crypto_helper_common.h
 create mode 100644 src/util/crypto_helper_cs.c
 create mode 100644 src/util/crypto_helper_esign.c
 create mode 100644 src/util/crypto_helper_rsa.c

diff --git a/src/donau/donau-httpd.c b/src/donau/donau-httpd.c
index 2614479..2d58c74 100644
--- a/src/donau/donau-httpd.c
+++ b/src/donau/donau-httpd.c
@@ -52,7 +52,7 @@
  * Above what request latency do we start to log?
  */
  #define WARN_LATENCY GNUNET_TIME_relative_multiply ( \
-           GNUNET_TIME_UNIT_MILLISECONDS, 500)
+    GNUNET_TIME_UNIT_MILLISECONDS, 500)
 
 /**
  * Are clients allowed to request /keys for times other than the
@@ -442,8 +442,7 @@ handle_mhd_request (void *cls,
     {
       .url = "keys",
       .method = MHD_HTTP_METHOD_GET,
-      .handler.get = &DH_keys_get_handler// ,
-                     // .nargs = 1
+      .handler.get = &DH_handler_keys
     },
     /* GET charities */
     {
@@ -1008,4 +1007,4 @@ main (int argc,
 }
 
 
-/* end of donau-httpd.c */
\ No newline at end of file
+/* end of donau-httpd.c */
diff --git a/src/donau/donau-httpd_keys.c b/src/donau/donau-httpd_keys.c
index 5198af8..abb62e0 100644
--- a/src/donau/donau-httpd_keys.c
+++ b/src/donau/donau-httpd_keys.c
@@ -1,23 +1,24 @@
 /*
-   This file is part of TALER
-   Copyright (C) 2020-2024 Taler Systems SA
+ This file is part of TALER
+ Copyright (C) 2020-2024 Taler Systems SA
 
-   TALER is free software; you can redistribute it and/or modify it under the
-   terms of the GNU Affero General Public License as published by the Free 
Software
-   Foundation; either version 3, or (at your option) any later version.
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free 
Software
+ Foundation; either version 3, or (at your option) any later version.
 
-   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
-   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
 
-   You should have received a copy of the GNU Affero General Public License 
along with
-   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ You should have received a copy of the GNU Affero General Public License 
along with
+ TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
  * @file donau-httpd_keys.c
  * @brief management of our various keys
  * @author Christian Grothoff
  * @author Özgür Kesim
+ * @author Pius Loosli
  */
 #include <taler/platform.h>
 #include <taler/taler_json_lib.h>
@@ -26,6 +27,8 @@
 #include "donau-httpd_keys.h"
 #include "donau-httpd_config.h"
 #include "donaudb_plugin.h"
+#include "donau_util.h"
+
 
 /**
  * How many /keys request do we hold in suspension at
@@ -97,6 +100,21 @@ static struct GNUNET_SCHEDULER_Task *keys_tt;
  */
 static bool terminating;
 
+/**
+ * RSA security module public key, all zero if not known.
+ */
+static struct TALER_SecurityModulePublicKeyP donation_unit_rsa_sm_pub;
+
+/**
+ * CS security module public key, all zero if not known.
+ */
+static struct TALER_SecurityModulePublicKeyP donation_unit_cs_sm_pub;
+
+/**
+ * EdDSA security module public key, all zero if not known.
+ */
+static struct TALER_SecurityModulePublicKeyP esign_sm_pub;
+
 /**
  * Function called to forcefully resume suspended keys requests.
  *
@@ -115,17 +133,14 @@ keys_timeout_cb (void *cls)
       break;
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Resuming /keys request due to timeout\n");
-    GNUNET_CONTAINER_DLL_remove (skr_head,
-                                 skr_tail,
-                                 skr);
+    GNUNET_CONTAINER_DLL_remove (skr_head, skr_tail, skr);
     MHD_resume_connection (skr->connection);
     TALER_MHD_daemon_trigger ();
     GNUNET_free (skr);
   }
   if (NULL == skr)
     return;
-  keys_tt = GNUNET_SCHEDULER_add_at (skr->timeout,
-                                     &keys_timeout_cb,
+  keys_tt = GNUNET_SCHEDULER_add_at (skr->timeout, &keys_timeout_cb,
                                      NULL);
 }
 
@@ -207,6 +222,11 @@ struct HelperDonationUnit
    */
   struct DONAU_DonationUnitPublicKey donation_unit_pub;
 
+  /**
+   * Signature over this key from the security module's key.
+   */
+  struct TALER_SecurityModuleSignatureP sm_sig;
+
   /**
    * Details depend on the @e donation_unit_pub.cipher type.
    */
@@ -230,7 +250,6 @@ struct HelperDonationUnit
    */
   char *section_name;
 
-
 };
 
 /**
@@ -262,18 +281,15 @@ static uint64_t key_generation;
  * RSA security module public key, all zero if not known.
  */
 // static struct TALER_SecurityModulePublicKeyP donation_unit_rsa_sm_pub;
-
 /**
  * CS security module public key, all zero if not known.
  */
 // static struct TALER_SecurityModulePublicKeyP donation_unit_cs_sm_pub;
-
 /**
  * EdDSA security module public key, all zero if not known.
  */
 // static struct TALER_SecurityModulePublicKeyP esign_sm_pub;
 
-
 /**
  * When do we forcefully timeout a /keys request?
  */
@@ -310,7 +326,7 @@ struct DH_KeyStateHandle
    * Mapping from donation unit keys to donation unit key issue struct.
    * Used to lookup the key by hash.
    */
-  struct GNUNET_CONTAINER_MultiHashMap *denomkey_map;
+  struct GNUNET_CONTAINER_MultiHashMap *donation_unit_key_map;
 
   /**
    * Map from `struct DONAU_DonauPublicKey` to `struct SigningKey`
@@ -411,30 +427,30 @@ struct HelperState
   /**
    * Handle for the esign/EdDSA helper.
    */
-  struct TALER_CRYPTO_ExchangeSignHelper *esh;
+  struct DONAU_CRYPTO_DonauSignHelper *esh;
 
   /**
    * Handle for the donation_unit/RSA helper.
    */
-  struct TALER_CRYPTO_RsaDenominationHelper *rsadh;
+  struct DONAU_CRYPTO_RsaDonationUnitHelper *rsadh;
 
   /**
    * Handle for the donation_unit/CS helper.
    */
-  struct TALER_CRYPTO_CsDenominationHelper *csdh;
+  struct DONAU_CRYPTO_CsDonationUnitHelper *csdh;
 
   /**
-   * Map from H(donation_unit_pub) to `struct HelperDenomination` entries.
+   * Map from H(donation_unit_pub) to `struct HelperDonationUnit` entries.
    */
   struct GNUNET_CONTAINER_MultiHashMap *donation_unit_keys;
 
   /**
-   * Map from H(rsa_pub) to `struct HelperDenomination` entries.
+   * Map from H(rsa_pub) to `struct HelperDonationUnit` entries.
    */
   struct GNUNET_CONTAINER_MultiHashMap *rsa_keys;
 
   /**
-   * Map from H(cs_pub) to `struct HelperDenomination` entries.
+   * Map from H(cs_pub) to `struct HelperDonationUnit` entries.
    */
   struct GNUNET_CONTAINER_MultiHashMap *cs_keys;
 
@@ -506,21 +522,17 @@ add_sign_key_cb (void *cls,
   //                                sk->meta.start.abs_time,
   //                                sk->meta.expire_sign.abs_time));
   // }
-  GNUNET_assert (
-    0 ==
-    json_array_append_new (
-      ctx->signkeys,
-      GNUNET_JSON_PACK (
-        // GNUNET_JSON_pack_timestamp ("stamp_start",
-        //                            sk->meta.start),
-        // GNUNET_JSON_pack_timestamp ("stamp_expire",
-        //                            sk->meta.expire_sign),
-        // GNUNET_JSON_pack_timestamp ("stamp_end",
-        //                            sk->meta.expire_legal),
-        // GNUNET_JSON_pack_data_auto ("master_sig",
-        //                            &sk->master_sig),
-        GNUNET_JSON_pack_data_auto ("key",
-                                    &sk->donau_pub))));
+  GNUNET_assert (0 == json_array_append_new (ctx->signkeys, GNUNET_JSON_PACK (
+                                               // GNUNET_JSON_pack_timestamp 
("stamp_start",
+                                               //                            
sk->meta.start),
+                                               // GNUNET_JSON_pack_timestamp 
("stamp_expire",
+                                               //                            
sk->meta.expire_sign),
+                                               // GNUNET_JSON_pack_timestamp 
("stamp_end",
+                                               //                            
sk->meta.expire_legal),
+                                               // GNUNET_JSON_pack_data_auto 
("master_sig",
+                                               //                            
&sk->master_sig),
+                                               GNUNET_JSON_pack_data_auto (
+                                                 "key", &sk->donau_pub))));
   return GNUNET_OK;
 }
 
@@ -539,26 +551,21 @@ setup_general_response_headers (void *cls,
   char dat[128];
 
   TALER_MHD_add_global_headers (response);
-  GNUNET_break (MHD_YES ==
-                MHD_add_response_header (response,
-                                         MHD_HTTP_HEADER_CONTENT_TYPE,
-                                         "application/json"));
-  TALER_MHD_get_date_string (ksh->reload_time.abs_time,
-                             dat);
-  GNUNET_break (MHD_YES ==
-                MHD_add_response_header (response,
-                                         MHD_HTTP_HEADER_LAST_MODIFIED,
-                                         dat));
+  GNUNET_break (
+    MHD_YES == MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE,
+                                        "application/json"));
+  TALER_MHD_get_date_string (ksh->reload_time.abs_time, dat);
+  GNUNET_break (
+    MHD_YES == MHD_add_response_header (response, 
MHD_HTTP_HEADER_LAST_MODIFIED,
+                                        dat));
   /* Set cache control headers: our response varies depending on these headers 
*/
-  GNUNET_break (MHD_YES ==
-                MHD_add_response_header (response,
-                                         MHD_HTTP_HEADER_VARY,
-                                         MHD_HTTP_HEADER_ACCEPT_ENCODING));
+  GNUNET_break (
+    MHD_YES == MHD_add_response_header (response, MHD_HTTP_HEADER_VARY,
+                                        MHD_HTTP_HEADER_ACCEPT_ENCODING));
   /* Information is always public, revalidate after 1 hour */
-  GNUNET_break (MHD_YES ==
-                MHD_add_response_header (response,
-                                         MHD_HTTP_HEADER_CACHE_CONTROL,
-                                         "public,max-age=3600"));
+  GNUNET_break (
+    MHD_YES == MHD_add_response_header (response, 
MHD_HTTP_HEADER_CACHE_CONTROL,
+                                        "public,max-age=3600"));
 }
 
 
@@ -586,8 +593,7 @@ finish_keys_response (struct DH_KeyStateHandle *ksh)
   sctx.signkeys = json_array ();
   GNUNET_assert (NULL != sctx.signkeys);
   sctx.min_sk_frequency = GNUNET_TIME_UNIT_FOREVER_REL;
-  GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map,
-                                         &add_sign_key_cb,
+  GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map, &add_sign_key_cb,
                                          &sctx);
   recoup = json_array ();
   GNUNET_assert (NULL != recoup);
@@ -598,8 +604,8 @@ finish_keys_response (struct DH_KeyStateHandle *ksh)
     // .min_dk_frequency = GNUNET_TIME_UNIT_FOREVER_REL,
     // };
 
-    // GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map,
-    //                                       &add_denom_key_cb,
+    // GNUNET_CONTAINER_multihashmap_iterate (ksh->donation_unit_key_map,
+    //                                       &add_donation_unit_key_cb,
     //                                       &dkc);
     // ksh->rekey_frequency
     //  = GNUNET_TIME_relative_min (dkc.min_dk_frequency,
@@ -618,15 +624,14 @@ finish_keys_response (struct DH_KeyStateHandle *ksh)
   {
     struct GNUNET_HashCode hc;
 
-    GNUNET_CRYPTO_hash_context_finish (hash_context,
-                                       &hc);
+    GNUNET_CRYPTO_hash_context_finish (hash_context, &hc);
     // if (GNUNET_OK !=
     //    create_krd (ksh,
     //                &hc,
     //                last_cherry_pick_date,
     //                sctx.signkeys,
     //                recoup,
-    //                grouped_denominations,
+    //                grouped_donation_units,
     //                &grouped_hash_xor))
     // {
     //  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
@@ -638,8 +643,9 @@ finish_keys_response (struct DH_KeyStateHandle *ksh)
   }
   else
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "No donation unit keys available. Refusing to generate /keys 
response.\n");
+    GNUNET_log (
+      GNUNET_ERROR_TYPE_WARNING,
+      "No donation unit keys available. Refusing to generate /keys 
response.\n");
     GNUNET_CRYPTO_hash_context_abort (hash_context);
   }
 
@@ -670,7 +676,7 @@ clear_donation_unit_cb (void *cls,
 
   (void) cls;
   (void) h_donation_unit_pub;
-  // TALER_denom_pub_free (&dk->denom_pub);
+  // TALER_donation_unit_pub_free (&dk->donation_unit_pub);
   GNUNET_free (dk);
   return GNUNET_OK;
 }
@@ -706,7 +712,7 @@ clear_signkey_cb (void *cls,
 static void
 clear_response_cache (struct DH_KeyStateHandle *ksh)
 {
-  for (unsigned int i = 0; i<ksh->krd_array_length; i++)
+  for (unsigned int i = 0; i < ksh->krd_array_length; i++)
   {
     struct KeysResponseData *krd = &ksh->krd_array[i];
 
@@ -714,9 +720,7 @@ clear_response_cache (struct DH_KeyStateHandle *ksh)
     MHD_destroy_response (krd->response_uncompressed);
     GNUNET_free (krd->etag);
   }
-  GNUNET_array_grow (ksh->krd_array,
-                     ksh->krd_array_length,
-                     0);
+  GNUNET_array_grow (ksh->krd_array, ksh->krd_array_length, 0);
 }
 
 
@@ -728,9 +732,151 @@ clear_response_cache (struct DH_KeyStateHandle *ksh)
 static void
 sync_key_helpers (struct HelperState *hs)
 {
-  TALER_CRYPTO_helper_rsa_poll (hs->rsadh);
-  TALER_CRYPTO_helper_cs_poll (hs->csdh);
-  TALER_CRYPTO_helper_esign_poll (hs->esh);
+  DONAU_CRYPTO_helper_rsa_poll (hs->rsadh);
+  DONAU_CRYPTO_helper_cs_poll (hs->csdh);
+//     DONAU_CRYPTO_helper_esign_poll (hs->esh);
+}
+
+
+void
+DH_resume_keys_requests (bool do_shutdown)
+{
+  struct SuspendedKeysRequests *skr;
+
+  if (do_shutdown)
+    terminating = true;
+  while (NULL != (skr = skr_head))
+  {
+    GNUNET_CONTAINER_DLL_remove (skr_head,
+                                 skr_tail,
+                                 skr);
+    skr_size--;
+    MHD_resume_connection (skr->connection);
+    TALER_MHD_daemon_trigger ();
+    GNUNET_free (skr);
+  }
+}
+
+
+/**
+ * Check that the given RSA security module's public key is the one
+ * we have pinned.  If it does not match, we die hard.
+ *
+ * @param sm_pub RSA security module public key to check
+ */
+static void
+check_donation_unit_rsa_sm_pub (const struct
+                                TALER_SecurityModulePublicKeyP *sm_pub)
+{
+  if (0 !=
+      GNUNET_memcmp (sm_pub,
+                     &donation_unit_rsa_sm_pub))
+  {
+    if (! GNUNET_is_zero (&donation_unit_rsa_sm_pub))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Our RSA security module changed its key. This must not 
happen.\n");
+      GNUNET_assert (0);
+    }
+    donation_unit_rsa_sm_pub = *sm_pub; /* TOFU ;-) */
+  }
+}
+
+
+/**
+ * Check that the given CS security module's public key is the one
+ * we have pinned.  If it does not match, we die hard.
+ *
+ * @param sm_pub RSA security module public key to check
+ */
+static void
+check_donation_unit_cs_sm_pub (const struct
+                               TALER_SecurityModulePublicKeyP *sm_pub)
+{
+  if (0 !=
+      GNUNET_memcmp (sm_pub,
+                     &donation_unit_cs_sm_pub))
+  {
+    if (! GNUNET_is_zero (&donation_unit_cs_sm_pub))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Our CS security module changed its key. This must not 
happen.\n");
+      GNUNET_assert (0);
+    }
+    donation_unit_cs_sm_pub = *sm_pub; /* TOFU ;-) */
+  }
+}
+
+
+/**
+ * Check that the given EdDSA security module's public key is the one
+ * we have pinned.  If it does not match, we die hard.
+ *
+ * @param sm_pub EdDSA security module public key to check
+ */
+static void
+check_esign_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub)
+{
+  if (0 !=
+      GNUNET_memcmp (sm_pub,
+                     &esign_sm_pub))
+  {
+    if (! GNUNET_is_zero (&esign_sm_pub))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Our EdDSA security module changed its key. This must not 
happen.\n");
+      GNUNET_assert (0);
+    }
+    esign_sm_pub = *sm_pub; /* TOFU ;-) */
+  }
+}
+
+
+/**
+ * Helper function for #destroy_key_helpers to free all entries
+ * in the `donation_unit_keys` map.
+ *
+ * @param cls the `struct HelperDonationUnit`
+ * @param h_donation_unit_pub hash of the donation unit public key
+ * @param value the `struct HelperDonationUnit` to release
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static enum GNUNET_GenericReturnValue
+free_donation_unit_cb (void *cls,
+                       const struct GNUNET_HashCode *h_donation_unit_pub,
+                       void *value)
+{
+  struct HelperDonationUnit *hd = value;
+
+  (void) cls;
+  (void) h_donation_unit_pub;
+  DONAU_donation_unit_pub_free (&hd->donation_unit_pub);
+  GNUNET_free (hd->section_name);
+  GNUNET_free (hd);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Helper function for #destroy_key_helpers to free all entries
+ * in the `esign_keys` map.
+ *
+ * @param cls the `struct HelperSignkey`
+ * @param pid unused, matches the exchange public key
+ * @param value the `struct HelperSignkey` to release
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static enum GNUNET_GenericReturnValue
+free_esign_cb (void *cls,
+               const struct GNUNET_PeerIdentity *pid,
+               void *value)
+{
+  struct HelperSignkey *hsk = value;
+
+  (void) cls;
+  (void) pid;
+  GNUNET_free (hsk);
+  return GNUNET_OK;
 }
 
 
@@ -743,15 +889,15 @@ sync_key_helpers (struct HelperState *hs)
 static void
 destroy_key_helpers (struct HelperState *hs)
 {
-  // GNUNET_CONTAINER_multihashmap_iterate (hs->denom_keys,
-  //                                       &free_denom_cb,
+  // GNUNET_CONTAINER_multihashmap_iterate (hs->donation_unit_keys,
+  //                                       &free_donation_unit_cb,
   //                                       hs);
   // GNUNET_CONTAINER_multihashmap_destroy (hs->rsa_keys);
   // hs->rsa_keys = NULL;
   // GNUNET_CONTAINER_multihashmap_destroy (hs->cs_keys);
   // hs->cs_keys = NULL;
-  // GNUNET_CONTAINER_multihashmap_destroy (hs->denom_keys);
-  // hs->denom_keys = NULL;
+  // GNUNET_CONTAINER_multihashmap_destroy (hs->donation_unit_keys);
+  // hs->donation_unit_keys = NULL;
   // GNUNET_CONTAINER_multipeermap_iterate (hs->esign_keys,
   //                                       &free_esign_cb,
   //                                       hs);
@@ -759,17 +905,17 @@ destroy_key_helpers (struct HelperState *hs)
   hs->esign_keys = NULL;
   if (NULL != hs->rsadh)
   {
-    TALER_CRYPTO_helper_rsa_disconnect (hs->rsadh);
+    DONAU_CRYPTO_helper_rsa_disconnect (hs->rsadh);
     hs->rsadh = NULL;
   }
   if (NULL != hs->csdh)
   {
-    TALER_CRYPTO_helper_cs_disconnect (hs->csdh);
+    DONAU_CRYPTO_helper_cs_disconnect (hs->csdh);
     hs->csdh = NULL;
   }
   if (NULL != hs->esh)
   {
-    TALER_CRYPTO_helper_esign_disconnect (hs->esh);
+    DONAU_CRYPTO_helper_esign_disconnect (hs->esh);
     hs->esh = NULL;
   }
 }
@@ -796,12 +942,10 @@ destroy_key_state (struct DH_KeyStateHandle *ksh,
   //                               gf);
   //  GNUNET_free (gf);
   // }
-  GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map,
-                                         &clear_donation_unit_cb,
-                                         ksh);
-  GNUNET_CONTAINER_multihashmap_destroy (ksh->denomkey_map);
-  GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map,
-                                         &clear_signkey_cb,
+  GNUNET_CONTAINER_multihashmap_iterate (ksh->donation_unit_key_map,
+                                         &clear_donation_unit_cb, ksh);
+  GNUNET_CONTAINER_multihashmap_destroy (ksh->donation_unit_key_map);
+  GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map, &clear_signkey_cb,
                                          ksh);
   GNUNET_CONTAINER_multipeermap_destroy (ksh->signkey_map);
   // json_decref (ksh->auditors);
@@ -822,6 +966,218 @@ destroy_key_state (struct DH_KeyStateHandle *ksh,
 }
 
 
+/**
+ * Function called with information about available keys for signing.  Usually
+ * only called once per key upon connect. Also called again in case a key is
+ * being revoked, in that case with an @a end_time of zero.
+ *
+ * @param cls closure with the `struct HelperState *`
+ * @param section_name name of the denomination type in the configuration;
+ *                 NULL if the key has been revoked or purged
+ * @param start_time when does the key become available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param validity_duration how long does the key remain available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param h_rsa hash of the @a denom_pub that is available (or was purged)
+ * @param bs_pub the public key itself, NULL if the key was revoked or purged
+ * @param sm_pub public key of the security module, NULL if the key was 
revoked or purged
+ * @param sm_sig signature from the security module, NULL if the key was 
revoked or purged
+ *               The signature was already verified against @a sm_pub.
+ */
+static void
+helper_rsa_cb (
+  void *cls,
+  const char *section_name,
+  struct GNUNET_TIME_Timestamp start_time,
+  struct GNUNET_TIME_Relative validity_duration,
+  const struct TALER_RsaPubHashP *h_rsa,
+  struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub,
+  const struct TALER_SecurityModulePublicKeyP *sm_pub,
+  const struct TALER_SecurityModuleSignatureP *sm_sig)
+{
+  struct HelperState *hs = cls;
+  struct HelperDonationUnit *hd;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "RSA helper announces key %s for denomination type %s with 
validity %s\n",
+              GNUNET_h2s (&h_rsa->hash),
+              section_name,
+              GNUNET_STRINGS_relative_time_to_string (validity_duration,
+                                                      GNUNET_NO));
+  key_generation++;
+  DH_resume_keys_requests (false);
+  hd = GNUNET_CONTAINER_multihashmap_get (hs->rsa_keys,
+                                          &h_rsa->hash);
+  if (NULL != hd)
+  {
+    /* should be just an update (revocation!), so update existing entry */
+    hd->validity_duration = validity_duration;
+    return;
+  }
+  GNUNET_assert (NULL != sm_pub);
+  check_donation_unit_rsa_sm_pub (sm_pub);
+  hd = GNUNET_new (struct HelperDonationUnit);
+  hd->start_time = start_time;
+  hd->validity_duration = validity_duration;
+  hd->h_details.h_rsa = *h_rsa;
+  hd->sm_sig = *sm_sig;
+  GNUNET_assert (GNUNET_CRYPTO_BSA_RSA == bs_pub->cipher);
+  hd->donation_unit_pub.bsign_pub_key =
+    GNUNET_CRYPTO_bsign_pub_incref (bs_pub);
+//  DONAU_Donation_unit_pub_hash(&hd->donation_unit_pub,
+//                        &hd->h_donation_unit_pub);
+//  hd->section_name = GNUNET_strdup (section_name);
+//  GNUNET_assert (
+//    GNUNET_OK ==
+//    GNUNET_CONTAINER_multihashmap_put (
+//      hs->donation_unit_keys,
+//      &hd->h_donation_unit_pub,
+//      hd,
+//      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+//  GNUNET_assert (
+//    GNUNET_OK ==
+//    GNUNET_CONTAINER_multihashmap_put (
+//      hs->rsa_keys,
+//      &hd->h_details.h_rsa.hash,
+//      hd,
+//      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+}
+
+
+/**
+ * Function called with information about available CS keys for signing. 
Usually
+ * only called once per key upon connect. Also called again in case a key is
+ * being revoked, in that case with an @a end_time of zero.
+ *
+ * @param cls closure with the `struct HelperState *`
+ * @param section_name name of the donation unit type in the configuration;
+ *                 NULL if the key has been revoked or purged
+ * @param start_time when does the key become available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param validity_duration how long does the key remain available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param h_cs hash of the @a donation_unit_pub that is available (or was 
purged)
+ * @param bs_pub the public key itself, NULL if the key was revoked or purged
+ */
+static void
+helper_cs_cb (
+  void *cls,
+  const char *section_name,
+  struct GNUNET_TIME_Timestamp start_time,
+  struct GNUNET_TIME_Relative validity_duration,
+  const struct TALER_CsPubHashP *h_cs,
+  struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub,
+  const struct TALER_SecurityModulePublicKeyP *sm_pub,
+  const struct TALER_SecurityModuleSignatureP *sm_sig)
+{
+  struct HelperState *hs = cls;
+  struct HelperDonationUnit *hd;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "CS helper announces key %s for donation unit type %s with 
validity %s\n",
+              GNUNET_h2s (&h_cs->hash),
+              section_name,
+              GNUNET_STRINGS_relative_time_to_string (validity_duration,
+                                                      GNUNET_NO));
+  key_generation++;
+  DH_resume_keys_requests (false);
+  hd = GNUNET_CONTAINER_multihashmap_get (hs->cs_keys,
+                                          &h_cs->hash);
+  if (NULL != hd)
+  {
+    /* should be just an update (revocation!), so update existing entry */
+    hd->validity_duration = validity_duration;
+    return;
+  }
+  GNUNET_assert (NULL != sm_pub);
+  check_donation_unit_cs_sm_pub (sm_pub);
+  hd = GNUNET_new (struct HelperDonationUnit);
+  hd->start_time = start_time;
+  hd->validity_duration = validity_duration;
+  hd->h_details.h_cs = *h_cs;
+  GNUNET_assert (GNUNET_CRYPTO_BSA_CS == bs_pub->cipher);
+  hd->donation_unit_pub.bsign_pub_key
+    = GNUNET_CRYPTO_bsign_pub_incref (bs_pub);
+//  DONAU_donation_unit_pub_hash (&hd->donation_unit_pub,
+//                        &hd->h_donation_unit_pub);
+//  hd->section_name = GNUNET_strdup (section_name);
+//  GNUNET_assert (
+//    GNUNET_OK ==
+//    GNUNET_CONTAINER_multihashmap_put (
+//      hs->donation_unit_keys,
+//      &hd->h_donation_unit_pub.hash,
+//      hd,
+//      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+//  GNUNET_assert (
+//    GNUNET_OK ==
+//    GNUNET_CONTAINER_multihashmap_put (
+//      hs->cs_keys,
+//      &hd->h_details.h_cs.hash,
+//      hd,
+//      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+}
+
+
+/**
+ * Function called with information about available keys for signing.  Usually
+ * only called once per key upon connect. Also called again in case a key is
+ * being revoked, in that case with an @a end_time of zero.
+ *
+ * @param cls closure with the `struct HelperState *`
+ * @param start_time when does the key become available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param validity_duration how long does the key remain available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param exchange_pub the public key itself, NULL if the key was revoked or 
purged
+ * @param sm_pub public key of the security module, NULL if the key was 
revoked or purged
+ * @param sm_sig signature from the security module, NULL if the key was 
revoked or purged
+ *               The signature was already verified against @a sm_pub.
+ */
+static void
+helper_esign_cb (
+  void *cls,
+  struct GNUNET_TIME_Timestamp start_time,
+  struct GNUNET_TIME_Relative validity_duration,
+  const struct DONAU_DonauPublicKeyP *donau_pub,
+  const struct TALER_SecurityModulePublicKeyP *sm_pub,
+  const struct TALER_SecurityModuleSignatureP *sm_sig)
+{
+  struct HelperState *hs = cls;
+  struct HelperSignkey *hsk;
+  struct GNUNET_PeerIdentity pid;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "EdDSA helper announces signing key %s with validity %s\n",
+              TALER_B2S (donau_pub),
+              GNUNET_STRINGS_relative_time_to_string (validity_duration,
+                                                      GNUNET_NO));
+  key_generation++;
+  DH_resume_keys_requests (false);
+  pid.public_key = donau_pub->eddsa_pub;
+  hsk = GNUNET_CONTAINER_multipeermap_get (hs->esign_keys,
+                                           &pid);
+//  if (NULL != hsk)
+//  {
+//    /* should be just an update (revocation!), so update existing entry */
+//    hsk->validity_duration = validity_duration;
+//    return;
+//  }
+  GNUNET_assert (NULL != sm_pub);
+  check_esign_sm_pub (sm_pub);
+  hsk = GNUNET_new (struct HelperSignkey);
+//  hsk->start_time = start_time;
+//  hsk->validity_duration = validity_duration;
+  hsk->donau_pub = *donau_pub;
+  GNUNET_assert (
+    GNUNET_OK ==
+    GNUNET_CONTAINER_multipeermap_put (
+      hs->esign_keys,
+      &pid,
+      hsk,
+      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+}
+
+
 /**
  * Setup helper state.
  *
@@ -831,9 +1187,9 @@ destroy_key_state (struct DH_KeyStateHandle *ksh,
 static enum GNUNET_GenericReturnValue
 setup_key_helpers (struct HelperState *hs)
 {
-  // hs->denom_keys
-  //  = GNUNET_CONTAINER_multihashmap_create (1024,
-  //                                          GNUNET_YES);
+  hs->donation_unit_keys
+    = GNUNET_CONTAINER_multihashmap_create (1024,
+                                            GNUNET_YES);
   hs->rsa_keys
     = GNUNET_CONTAINER_multihashmap_create (1024,
                                             GNUNET_YES);
@@ -843,30 +1199,33 @@ setup_key_helpers (struct HelperState *hs)
   hs->esign_keys
     = GNUNET_CONTAINER_multipeermap_create (32,
                                             GNUNET_NO /* MUST BE NO! */);
-  // hs->rsadh = TALER_CRYPTO_helper_rsa_connect (DH_cfg,
-  //                                             &helper_rsa_cb,
-  //                                             hs);
-  // if (NULL == hs->rsadh)
-  // {
-  //  destroy_key_helpers (hs);
-  //  return GNUNET_SYSERR;
-  // }
-  // hs->csdh = TALER_CRYPTO_helper_cs_connect (DH_cfg,
-  //                                           &helper_cs_cb,
-  //                                           hs);
-  // if (NULL == hs->csdh)
-  // {
-  //  destroy_key_helpers (hs);
-  //  return GNUNET_SYSERR;
-  // }
-  // hs->esh = TALER_CRYPTO_helper_esign_connect (DH_cfg,
-  //                                             &helper_esign_cb,
-  //                                             hs);
-  // if (NULL == hs->esh)
-  // {
-  //  destroy_key_helpers (hs);
-  //  return GNUNET_SYSERR;
-  // }
+  hs->rsadh = DONAU_CRYPTO_helper_rsa_connect (DH_cfg,
+                                               "donau",
+                                               &helper_rsa_cb,
+                                               hs);
+  if (NULL == hs->rsadh)
+  {
+    destroy_key_helpers (hs);
+    return GNUNET_SYSERR;
+  }
+  hs->csdh = DONAU_CRYPTO_helper_cs_connect (DH_cfg,
+                                             "donau",
+                                             &helper_cs_cb,
+                                             hs);
+  if (NULL == hs->csdh)
+  {
+    destroy_key_helpers (hs);
+    return GNUNET_SYSERR;
+  }
+  hs->esh = DONAU_CRYPTO_helper_esign_connect (DH_cfg,
+                                               "donau",
+                                               &helper_esign_cb,
+                                               hs);
+  if (NULL == hs->esh)
+  {
+    destroy_key_helpers (hs);
+    return GNUNET_SYSERR;
+  }
   return GNUNET_OK;
 }
 
@@ -875,16 +1234,13 @@ setup_key_helpers (struct HelperState *hs)
  * Create a key state.
  *
  * @param[in] hs helper state to (re)use, NULL if not available
- * @param management_only if we should NOT run 'finish_keys_response()'
- *                  because we only need the state for the /management/keys API
  * @return NULL on error (i.e. failed to access database)
  */
-static struct DH_KeyStateHandle *
-build_key_state (struct HelperState *hs /*,
-                 bool management_only*/)
+static struct DH_KeyStateHandle*
+build_key_state (struct HelperState *hs)
 {
   struct DH_KeyStateHandle *ksh;
-  // enum GNUNET_DB_QueryStatus qs;
+  enum GNUNET_DB_QueryStatus qs;
 
   ksh = GNUNET_new (struct DH_KeyStateHandle);
   ksh->signature_expires = GNUNET_TIME_UNIT_FOREVER_TS;
@@ -894,8 +1250,7 @@ build_key_state (struct HelperState *hs /*,
   if (NULL == hs)
   {
     ksh->helpers = GNUNET_new (struct HelperState);
-    if (GNUNET_OK !=
-        setup_key_helpers (ksh->helpers))
+    if (GNUNET_OK != setup_key_helpers (ksh->helpers))
     {
       GNUNET_free (ksh->helpers);
       GNUNET_assert (NULL == ksh->management_keys_reply);
@@ -907,56 +1262,49 @@ build_key_state (struct HelperState *hs /*,
   {
     ksh->helpers = hs;
   }
-  ksh->denomkey_map = GNUNET_CONTAINER_multihashmap_create (1024,
-                                                            true);
+  ksh->donation_unit_key_map = GNUNET_CONTAINER_multihashmap_create (1024,
+                                                                     true);
   ksh->signkey_map = GNUNET_CONTAINER_multipeermap_create (32,
                                                            false /* MUST be 
false! */
                                                            );
   /* NOTE: fetches master-signed signkeys, but ALSO those that were revoked! */
-  // GNUNET_break (GNUNET_OK ==
-  //              DH_plugin->preflight (DH_plugin->cls));
-  // if (qs < 0)
-  // {
-  //  GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
-  //  GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
-  //  destroy_key_state (ksh,
-  //                     true);
-  //  return NULL;
-  // }
-  // qs = DH_plugin->iterate_denominations (DH_plugin->cls,
-  //                                       &denomination_info_cb,
-  //                                       ksh);
-  // if (qs < 0)
-  // {
-  //  GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
-  //  GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
-  //  destroy_key_state (ksh,
-  //                     true);
-  //  return NULL;
-  // }
+  GNUNET_break (GNUNET_OK ==
+                DH_plugin->preflight (DH_plugin->cls));
+  if (qs < 0)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+    GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
+    destroy_key_state (ksh,
+                       true);
+    return NULL;
+  }
+//      qs = DH_plugin->iterate_donation_units (DH_plugin->cls,
+//                                            &donation_unit_info_cb,
+//                                            ksh);
+  if (qs < 0)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+    GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
+    destroy_key_state (ksh,
+                       true);
+    return NULL;
+  }
   /* NOTE: ONLY fetches non-revoked AND master-signed signkeys! */
-  // qs = DH_plugin->iterate_active_signkeys (DH_plugin->cls,
-  //                                         &signkey_info_cb,
-  //                                         ksh);
-  // if (qs < 0)
-  // {
-  //  GNUNET_break (0);
-  //  destroy_key_state (ksh,
-  //                     true);
-  //  return NULL;
-  // }
-
-  // if (management_only)
-  // {
-  //   ksh->management_only = true;
-  //   return ksh;
-  // }
-
-  if (GNUNET_OK !=
-      finish_keys_response (ksh))
+//      qs = DH_plugin->iterate_active_signkeys (DH_plugin->cls,
+//                                              &signkey_info_cb,
+//                                              ksh);
+  if (qs < 0)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Could not finish /keys response (likely no signing keys 
available yet)\n");
+    GNUNET_break (0);
+    destroy_key_state (ksh,
+                       true);
+    return NULL;
+  }
+  if (GNUNET_OK != finish_keys_response (ksh))
+  {
+    GNUNET_log (
+      GNUNET_ERROR_TYPE_WARNING,
+      "Could not finish /keys response (likely no signing keys available 
yet)\n");
     destroy_key_state (ksh,
                        true);
     return NULL;
@@ -969,13 +1317,12 @@ build_key_state (struct HelperState *hs /*,
 void
 DH_keys_update_states ()
 {
-  struct GNUNET_DB_EventHeaderP es = {
-    .size = htons (sizeof (es)),
+  struct GNUNET_DB_EventHeaderP es =
+  { .size = htons (sizeof(es)),
     // .type = htons (TALER_DBEVENT_DONAU_KEYS_UPDATED),
   };
 
-  DH_plugin->event_notify (DH_plugin->cls,
-                           &es,
+  DH_plugin->event_notify (DH_plugin->cls, &es,
                            NULL,
                            0);
   key_generation++;
@@ -983,7 +1330,7 @@ DH_keys_update_states ()
 }
 
 
-static struct DH_KeyStateHandle *
+static struct DH_KeyStateHandle*
 DH_keys_get_state (/*bool management_only*/)
 {
   struct DH_KeyStateHandle *old_ksh;
@@ -998,15 +1345,15 @@ DH_keys_get_state (/*bool management_only*/)
     key_state = ksh;
     return ksh;
   }
-  if ( (old_ksh->key_generation < key_generation) ||
-       (GNUNET_TIME_absolute_is_past (old_ksh->signature_expires.abs_time)) )
+  if ((old_ksh->key_generation < key_generation)
+      || (GNUNET_TIME_absolute_is_past (old_ksh->signature_expires.abs_time)))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Rebuilding /keys, generation upgrade from %llu to %llu\n",
-                (unsigned long long) old_ksh->key_generation,
-                (unsigned long long) key_generation);
+                (unsigned long long ) old_ksh->key_generation,
+                (unsigned long long ) key_generation);
     ksh = build_key_state (old_ksh->helpers /*,
-                           management_only*/);
+       management_only*/   );
     key_state = ksh;
     old_ksh->helpers = NULL;
     destroy_key_state (old_ksh,
@@ -1048,7 +1395,7 @@ struct FutureBuilderContext
  *
  * @param cls the `struct FutureBuilderContext *`
  * @param h_donation_unit_pub hash of the donation unit public key
- * @param value a `struct HelperDenomination`
+ * @param value a `struct HelperDonationUnit`
  * @return #GNUNET_OK (continue to iterate)
  */
 static enum GNUNET_GenericReturnValue
@@ -1059,29 +1406,30 @@ add_donation_unitkey_cb (void *cls,
   struct FutureBuilderContext *fbc = cls;
   struct HelperDonationUnit *helper_donation_unit = value;
   struct DH_DonationUnitKey *donation_unit_key;
-  struct DONAUDB_DonationUnitKeyMetaData meta = {0};
+  struct DONAUDB_DonationUnitKeyMetaData meta =
+  { 0 };
 
-  donation_unit_key = GNUNET_CONTAINER_multihashmap_get 
(fbc->ksh->denomkey_map,
-                                                         h_donation_unit_pub);
+  donation_unit_key = GNUNET_CONTAINER_multihashmap_get (
+    fbc->ksh->donation_unit_key_map,
+    h_donation_unit_pub);
   if (NULL != donation_unit_key)
     return GNUNET_OK; /* skip: this key is already active! */
   // if (GNUNET_TIME_relative_is_zero (hd->validity_duration))
   // return GNUNET_OK; /* this key already expired! */
 
   GNUNET_assert (
-    0 ==
-    json_array_append_new (
-      fbc->donation_units,
-      GNUNET_JSON_PACK (
-        TALER_JSON_pack_amount ("value",
-                                &meta.value),
-        GNUNET_JSON_pack_uint64 ("year",
-                                 meta.validity_year),
-        GNUNET_JSON_pack_data_auto ("donation_unit_pub",
-                                    &helper_donation_unit->donation_unit_pub)
-        // GNUNET_JSON_pack_string ("section_name",
-        //  helper_donation_unit->section_name)
-        )));
+    0 == json_array_append_new (fbc->donation_units, GNUNET_JSON_PACK (
+                                  TALER_JSON_pack_amount ("value", 
&meta.value),
+                                  GNUNET_JSON_pack_uint64 (
+                                    "year", meta.validity_year),
+                                  GNUNET_JSON_pack_data_auto (
+                                    "donation_unit_pub",
+                                    &
+                                    helper_donation_unit
+                                    ->donation_unit_pub)
+                                  // GNUNET_JSON_pack_string ("section_name",
+                                  //  helper_donation_unit->section_name)
+                                  )));
   return GNUNET_OK;
 }
 
@@ -1094,7 +1442,7 @@ add_donation_unitkey_cb (void *cls,
  *
  * @param cls the `struct FutureBuilderContext *`
  * @param pid actually the exchange public key (type disguised)
- * @param value a `struct HelperDenomination`
+ * @param value a `struct HelperDonationUnit`
  * @return #GNUNET_OK (continue to iterate)
  */
 static enum GNUNET_GenericReturnValue
@@ -1108,8 +1456,7 @@ add_signkey_cb (void *cls,
   // struct GNUNET_TIME_Timestamp stamp_expire;
   // struct GNUNET_TIME_Timestamp legal_end;
 
-  sk = GNUNET_CONTAINER_multipeermap_get (fbc->ksh->signkey_map,
-                                          pid);
+  sk = GNUNET_CONTAINER_multipeermap_get (fbc->ksh->signkey_map, pid);
   if (NULL != sk)
     return GNUNET_OK; /* skip: this key is already active */
   // if (GNUNET_TIME_relative_is_zero (hsk->validity_duration))
@@ -1120,31 +1467,31 @@ add_signkey_cb (void *cls,
   // legal_end = GNUNET_TIME_absolute_to_timestamp (
   // GNUNET_TIME_absolute_add (stamp_expire.abs_time,
   // signkey_legal_duration));
-  GNUNET_assert (0 ==
-                 json_array_append_new (
-                   fbc->signkeys,
-                   GNUNET_JSON_PACK (
-                     GNUNET_JSON_pack_data_auto ("key",
-                                                 &hsk->donau_pub),
-                     //  GNUNET_JSON_pack_timestamp ("stamp_end",
-                     //  legal_end),
-                     GNUNET_JSON_pack_data_auto ("year",
-                                                 &hsk->year)
-                     //  GNUNET_JSON_pack_data_auto ("signkey_secmod_sig",
-                     //  &hsk->sm_sig)
-                     )));
+  GNUNET_assert (
+    0 == json_array_append_new (fbc->signkeys, GNUNET_JSON_PACK (
+                                  GNUNET_JSON_pack_data_auto ("key",
+                                                              &hsk->donau_pub),
+                                  //  GNUNET_JSON_pack_timestamp ("stamp_end",
+                                  //  legal_end),
+                                  GNUNET_JSON_pack_data_auto (
+                                    "year", &hsk->year)
+                                  //  GNUNET_JSON_pack_data_auto 
("signkey_secmod_sig",
+                                  //  &hsk->sm_sig)
+                                  )));
   return GNUNET_OK;
 }
 
 
 MHD_RESULT
-DH_keys_get_handler (const struct DH_RequestHandler *rh,
-                     struct MHD_Connection *connection)
+DH_handler_keys (struct DH_RequestContext *rc,
+                 const char *const args[])
 {
   struct DH_KeyStateHandle *ksh;
   json_t *reply;
 
-  (void) rh;
+  // connection is always initialised
+  struct MHD_Connection *connection = rc->connection;
+
   ksh = DH_keys_get_state (true);
   if (NULL == ksh)
   {
@@ -1156,11 +1503,9 @@ DH_keys_get_handler (const struct DH_RequestHandler *rh,
   sync_key_helpers (ksh->helpers);
   if (NULL == ksh->management_keys_reply)
   {
-    struct FutureBuilderContext fbc = {
-      .ksh = ksh,
-      .donation_units = json_array (),
-      .signkeys = json_array ()
-    };
+    struct FutureBuilderContext fbc =
+    { .ksh = ksh, .donation_units = json_array (), .signkeys =
+        json_array () };
     // if ( (GNUNET_is_zero (&donation_unit_rsa_sm_pub)) &&
     //      (GNUNET_is_zero (&donation_unit_cs_sm_pub)) )
     // {
@@ -1180,16 +1525,13 @@ DH_keys_get_handler (const struct DH_RequestHandler *rh,
     GNUNET_assert (NULL != fbc.donation_units);
     GNUNET_assert (NULL != fbc.signkeys);
     GNUNET_CONTAINER_multihashmap_iterate (ksh->helpers->donation_unit_keys,
-                                           &add_donation_unitkey_cb,
-                                           &fbc);
+                                           &add_donation_unitkey_cb, &fbc);
     GNUNET_CONTAINER_multipeermap_iterate (ksh->helpers->esign_keys,
-                                           &add_signkey_cb,
-                                           &fbc);
+                                           &add_signkey_cb, &fbc);
     reply = GNUNET_JSON_PACK (
       GNUNET_JSON_pack_array_steal ("future_donation_units",
                                     fbc.donation_units),
-      GNUNET_JSON_pack_array_steal ("future_signkeys",
-                                    fbc.signkeys)
+      GNUNET_JSON_pack_array_steal ("future_signkeys", fbc.signkeys)
       // GNUNET_JSON_pack_data_auto ("donation_unit_secmod_public_key",
       // &donation_unit_rsa_sm_pub),
       // GNUNET_JSON_pack_data_auto ("donation_unit_secmod_cs_public_key",
@@ -1197,13 +1539,13 @@ DH_keys_get_handler (const struct DH_RequestHandler *rh,
       // GNUNET_JSON_pack_data_auto ("signkey_secmod_public_key",
       // &esign_sm_pub));
       );
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Returning GET /keys response:\n");
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Returning GET /keys response:\n");
     if (NULL == reply)
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         
TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
-                                         NULL);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_INTERNAL_SERVER_ERROR,
+        TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
+        NULL);
     GNUNET_assert (NULL == ksh->management_keys_reply);
     ksh->management_keys_reply = reply;
   }
@@ -1211,9 +1553,9 @@ DH_keys_get_handler (const struct DH_RequestHandler *rh,
   {
     reply = ksh->management_keys_reply;
   }
-  return TALER_MHD_reply_json (connection,
-                               reply,
+  return TALER_MHD_reply_json (connection, reply,
                                MHD_HTTP_OK);
+
 }
 
 
@@ -1232,13 +1574,9 @@ krd_search_comparator (const void *key,
   const struct GNUNET_TIME_Timestamp *kd = key;
   const struct KeysResponseData *krd = value;
 
-  if (GNUNET_TIME_timestamp_cmp (*kd,
-                                 >,
-                                 krd->cherry_pick_date))
+  if (GNUNET_TIME_timestamp_cmp (*kd, >, krd->cherry_pick_date))
     return -1;
-  if (GNUNET_TIME_timestamp_cmp (*kd,
-                                 <,
-                                 krd->cherry_pick_date))
+  if (GNUNET_TIME_timestamp_cmp (*kd, <, krd->cherry_pick_date))
     return 1;
   return 0;
 }
@@ -1251,16 +1589,14 @@ krd_search_comparator (const void *key,
  * @param[in,out] resp response to modify
  */
 typedef void
-(*TEH_RESPONSE_SetHeaders)(void *cls,
+(*DH_RESPONSE_SetHeaders) (void *cls,
                            struct MHD_Response *resp);
 
-
 MHD_RESULT
-DH_RESPONSE_reply_not_modified (
-  struct MHD_Connection *connection,
-  const char *etags,
-  TEH_RESPONSE_SetHeaders cb,
-  void *cb_cls)
+DH_RESPONSE_reply_not_modified (struct MHD_Connection *connection,
+                                const char *etags,
+                                DH_RESPONSE_SetHeaders cb,
+                                void *cb_cls)
 {
   MHD_RESULT ret;
   struct MHD_Response *resp;
@@ -1268,12 +1604,9 @@ DH_RESPONSE_reply_not_modified (
   resp = MHD_create_response_from_buffer (0,
                                           NULL,
                                           MHD_RESPMEM_PERSISTENT);
-  cb (cb_cls,
-      resp);
-  GNUNET_break (MHD_YES ==
-                MHD_add_response_header (resp,
-                                         MHD_HTTP_HEADER_ETAG,
-                                         etags));
+  cb (cb_cls, resp);
+  GNUNET_break (
+    MHD_YES == MHD_add_response_header (resp, MHD_HTTP_HEADER_ETAG, etags));
   ret = MHD_queue_response (connection,
                             MHD_HTTP_NOT_MODIFIED,
                             resp);
@@ -1391,5 +1724,4 @@ DH_RESPONSE_reply_not_modified (
 //   }
 // }
 
-
 /* end of donau-httpd_keys.c */
diff --git a/src/donau/donau-httpd_keys.h b/src/donau/donau-httpd_keys.h
index a39fa5c..b0750b4 100644
--- a/src/donau/donau-httpd_keys.h
+++ b/src/donau/donau-httpd_keys.h
@@ -166,8 +166,8 @@ TEH_resume_keys_requests (bool do_shutdown);
  * @return MHD result code
  */
 MHD_RESULT
-DH_keys_get_handler (const struct DH_RequestHandler *, struct MHD_Connection 
*);
-
+DH_handler_keys (struct DH_RequestContext *rc,
+                 const char *const args[]);
 
 /**
  * Initialize keys subsystem.
diff --git a/src/include/donau_crypto_lib.h b/src/include/donau_crypto_lib.h
index 2616a9e..ad7f950 100644
--- a/src/include/donau_crypto_lib.h
+++ b/src/include/donau_crypto_lib.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2023 Taler Systems SA
+  Copyright (C) 2023-2024 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
@@ -21,6 +21,7 @@
  * @author Christian Grothoff <christian@grothoff.org>
  * @author Özgür Kesim <oec-taler@kesim.org>
  * @author Lukas Matyja
+ * @author Pius Loosli
  */
 #if ! defined (__DONAU_UTIL_LIB_H_INSIDE__)
  #error "Only <donau_util.h> can be included directly."
@@ -126,8 +127,10 @@ struct DONAU_DonationUnitHashP
  * @return 0 if the keys are equal, otherwise -1 or 1
  */
 int
-DONAU_donation_unit_pub_cmp (const struct DONAU_DonationUnitPublicKey 
*donation_unit1,
-                     const struct DONAU_DonationUnitPublicKey *donation_unit2);
+DONAU_donation_unit_pub_cmp (const struct
+                             DONAU_DonationUnitPublicKey *donation_unit1,
+                             const struct
+                             DONAU_DonationUnitPublicKey *donation_unit2);
 
 /**
  * Make a (deep) copy of the given @a donation_unit_src to
@@ -137,8 +140,12 @@ DONAU_donation_unit_pub_cmp (const struct 
DONAU_DonationUnitPublicKey *donation_
  * @param donation_unit_src public key to copy
  */
 void
-DONAU_donation_unit_pub_deep_copy (struct DONAU_DonationUnitPublicKey 
*donation_unit_dst,
-                             const struct DONAU_DonationUnitPublicKey 
*donation_unit_src);
+DONAU_donation_unit_pub_deep_copy (struct
+                                   DONAU_DonationUnitPublicKey *
+                                   donation_unit_dst,
+                                   const struct
+                                   DONAU_DonationUnitPublicKey *
+                                   donation_unit_src);
 
 /**
  * Free internals of @a donation_unit_pub, but not @a donation_unit_pub itself.
@@ -146,7 +153,8 @@ DONAU_donation_unit_pub_deep_copy (struct 
DONAU_DonationUnitPublicKey *donation_
  * @param[in] donation_unit_pub key to free
  */
 void
-DONAU_donation_unit_pub_free (struct DONAU_DonationUnitPublicKey 
*donation_unit_pub);
+DONAU_donation_unit_pub_free (struct
+                              DONAU_DonationUnitPublicKey *donation_unit_pub);
 
 /**
  * Hash used to represent a Donation Receipt
@@ -174,9 +182,9 @@ struct DONAU_HashDonorTaxId
 struct DONAU_BlindedDonationUnitSignature
 {
   /**
-   * The blinded signature
+   * Donation Units use blind signatures.
    */
-  struct TALER_DenominationSignature b_sig;
+  struct GNUNET_CRYPTO_BlindedSignature *blinded_sig;
 
 };
 
@@ -324,9 +332,134 @@ DONAU_donation_statement_verify (
   const struct DONAU_DonauSignatureP *statement_sig);
 
 
-/* ********************* helper ************************** */
+///* ********************* donau blind signing ************************** */
 
 
+/**
+ * Free internals of @a donation_unit_sig, but not @a donation_unit_sig itself.
+ *
+ * @param[in] donation_unit_sig signature to free
+// */
+void
+DONAU_blinded_donation_unit_sig_free (
+  struct DONAU_BlindedDonationUnitSignature *donation_unit_sig);
+
+// FIXME: Copied from taler_crypto_lib.h, is anything of this necessary?
+///**
+// * Compute the hash of the given @a donation_unit_pub.
+// *
+// * @param donation_unit_pub public key to hash
+// * @param[out] donation_unit_hash resulting hash value
+// */
+// void
+// TALER_donation_unit_pub_hash (const struct DONAU_DonationUnitPublicKey 
*donation_unit_pub,
+//                      struct DONAU_DonationUnitHashP *donation_unit_hash);
+//
+//
+///**
+// * Make a (deep) copy of the given @a donation_unit_src to
+// * @a donation_unit_dst.
+// *
+// * @param[out] donation_unit_dst target to copy to
+// * @param donation_unit_src public key to copy
+// */
+// void
+// TALER_donation_unit_pub_deep_copy (struct DONAU_DonationUnitPublicKey 
*donation_unit_dst,
+//                           const struct DONAU_DonationUnitPublicKey 
*donation_unit_src);
+//
+//
+///**
+// * Make a (deep) copy of the given @a donation_unit_src to
+// * @a donation_unit_dst.
+// *
+// * @param[out] donation_unit_dst target to copy to
+// * @param donation_unit_src public key to copy
+// */
+// void
+// TALER_donation_unit_sig_deep_copy (struct DONAU_DonationUnitSignature 
*donation_unit_dst,
+//                           const struct DONAU_DonationUnitSignature 
*donation_unit_src);
+//
+//
+///**
+// * Make a (deep) copy of the given @a donation_unit_src to
+// * @a donation_unit_dst.
+// *
+// * @param[out] donation_unit_dst target to copy to
+// * @param donation_unit_src public key to copy
+// */
+// void
+// TALER_blinded_donation_unit_sig_deep_copy (
+//  struct DONAU_BlindedDonationUnitSignature *donation_unit_dst,
+//  const struct DONAU_BlindedDonationUnitSignature *donation_unit_src);
+//
+//
+///**
+// * Compare two donation unit public keys.
+// *
+// * @param donation_unit1 first key
+// * @param donation_unit2 second key
+// * @return 0 if the keys are equal, otherwise -1 or 1
+// */
+// int
+// TALER_donation_unit_pub_cmp (const struct DONAU_DonationUnitPublicKey 
*donation_unit1,
+//                     const struct DONAU_DonationUnitPublicKey 
*donation_unit2);
+//
+//
+///**
+// * Compare two donation unit signatures.
+// *
+// * @param sig1 first signature
+// * @param sig2 second signature
+// * @return 0 if the keys are equal, otherwise -1 or 1
+// */
+// int
+// TALER_donation_unit_sig_cmp (const struct DONAU_DonationUnitSignature *sig1,
+//                     const struct DONAU_DonationUnitSignature *sig2);
+//
+//
+///**
+// * Compare two blinded donation unit signatures.
+// *
+// * @param sig1 first signature
+// * @param sig2 second signature
+// * @return 0 if the keys are equal, otherwise -1 or 1
+// */
+// int
+// TALER_blinded_donation_unit_sig_cmp (
+//  const struct DONAU_BlindedDonationUnitSignature *sig1,
+//  const struct DONAU_BlindedDonationUnitSignature *sig2);
+//
+//
+///**
+// * Compare two blinded planchets.
+// *
+// * @param bp1 first blinded planchet
+// * @param bp2 second blinded planchet
+// * @return 0 if the keys are equal, otherwise -1 or 1
+// */
+// int
+// TALER_blinded_planchet_cmp (
+//  const struct DONAU_BlindedPlanchet *bp1,
+//  const struct DONAU_BlindedPlanchet *bp2);
+//
+//
+///**
+// * Verify signature made with a donation unit public key
+// * over a donation receipt.
+// *
+// * @param donation_unit_pub public donation unit key
+// * @param donation_unit_sig signature made with the private key
+// * @param c_hash hash over the coin
+// * @return #GNUNET_OK if the signature is valid
+// */
+// enum GNUNET_GenericReturnValue
+// TALER_donation_unit_pub_verify (const struct DONAU_DonationUnitPublicKey 
*donation_unit_pub,
+//                        const struct DONAU_DonationUnitSignature 
*donation_unit_sig,
+//                        const struct DONAU_ *c_hash);
+//
+
+
+/*********************** helpers 
************************************************/
 /**
  * Group of donation units. These are the common fields of an array of
  * donation units.
@@ -369,4 +502,418 @@ struct TALER_DonauBatchIssueValues
 };
 
 
+/* ********************* Helper-based RSA operations 
************************** */
+/**
+ * Function called with information about available keys for signing.  Usually
+ * only called once per key upon connect. Also called again in case a key is
+ * being revoked, in that case with an @a end_time of zero.
+ *
+ * @param cls closure
+ * @param section_name name of the donation unit type in the configuration;
+ *                 NULL if the key has been revoked or purged
+ * @param start_time when does the key become available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param validity_duration how long does the key remain available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param h_rsa hash of the RSA @a donation_unit_pub that is available (or was 
purged)
+ * @param bs_pub the public key itself, NULL if the key was revoked or purged
+ * @param sm_pub public key of the security module, NULL if the key was 
revoked or purged
+ * @param sm_sig signature from the security module, NULL if the key was 
revoked or purged
+ *               The signature was already verified against @a sm_pub.
+ */
+typedef void
+(*DONAU_CRYPTO_RsaDonationUnitKeyStatusCallback)(
+  void *cls,
+  const char *section_name,
+  struct GNUNET_TIME_Timestamp start_time,
+  struct GNUNET_TIME_Relative validity_duration,
+  const struct TALER_RsaPubHashP *h_rsa,
+  struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub,
+  const struct TALER_SecurityModulePublicKeyP *sm_pub,
+  const struct TALER_SecurityModuleSignatureP *sm_sig);
+
+/**
+ * Handle for talking to an Donation unit key signing helper.
+ */
+struct DONAU_CRYPTO_RsaDonationUnitHelper;
+
+
+/**
+ * Initiate connection to an donation unit key helper.
+ *
+ * @param cfg configuration to use
+ * @param section configuration section prefix to use, usually 'taler' or 
'donau'
+ * @param dkc function to call with key information
+ * @param dkc_cls closure for @a dkc
+ * @return NULL on error (such as bad @a cfg).
+ */
+struct DONAU_CRYPTO_RsaDonationUnitHelper *
+DONAU_CRYPTO_helper_rsa_connect (
+  const struct GNUNET_CONFIGURATION_Handle *cfg,
+  const char *section,
+  DONAU_CRYPTO_RsaDonationUnitKeyStatusCallback dkc,
+  void *dkc_cls);
+
+
+/**
+ * Function to call to 'poll' for updates to the available key material.
+ * Should be called whenever it is important that the key material status is
+ * current, like when handling a "/keys" request.  This function basically
+ * briefly checks if there are messages from the helper announcing changes to
+ * donation unit keys.
+ *
+ * @param dh helper process connection
+ */
+void
+DONAU_CRYPTO_helper_rsa_poll (struct DONAU_CRYPTO_RsaDonationUnitHelper *dh);
+
+/**
+ * Close connection to @a dh.
+ *
+ * @param[in] dh connection to close
+ */
+void
+DONAU_CRYPTO_helper_rsa_disconnect (
+  struct DONAU_CRYPTO_RsaDonationUnitHelper *dh);
+
+
+/* **************** Helper-based CS operations **************** */
+
+/**
+ * Handle for talking to an DonationUnit key signing helper.
+ */
+struct DONAU_CRYPTO_CsDonationUnitHelper;
+
+/**
+ * Function called with information about available keys for signing.  Usually
+ * only called once per key upon connect. Also called again in case a key is
+ * being revoked, in that case with an @a end_time of zero.
+ *
+ * @param cls closure
+ * @param section_name name of the donation unit type in the configuration;
+ *                 NULL if the key has been revoked or purged
+ * @param start_time when does the key become available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param validity_duration how long does the key remain available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param h_cs hash of the CS @a donation_unit_pub that is available (or was 
purged)
+ * @param bsign_pub the public key itself, NULL if the key was revoked or 
purged
+ * @param sm_pub public key of the security module, NULL if the key was 
revoked or purged
+ * @param sm_sig signature from the security module, NULL if the key was 
revoked or purged
+ *               The signature was already verified against @a sm_pub.
+ */
+typedef void
+(*DONAU_CRYPTO_CsDonationUnitKeyStatusCallback)(
+  void *cls,
+  const char *section_name,
+  struct GNUNET_TIME_Timestamp start_time,
+  struct GNUNET_TIME_Relative validity_duration,
+  const struct TALER_CsPubHashP *h_cs,
+  struct GNUNET_CRYPTO_BlindSignPublicKey *bsign_pub,
+  const struct TALER_SecurityModulePublicKeyP *sm_pub,
+  const struct TALER_SecurityModuleSignatureP *sm_sig);
+
+
+/**
+ * Initiate connection to an donation unit key helper.
+ *
+ * @param cfg configuration to use
+ * @param section configuration section prefix to use, usually 'taler' or 
'donau'
+ * @param dkc function to call with key information
+ * @param dkc_cls closure for @a dkc
+ * @return NULL on error (such as bad @a cfg).
+ */
+struct DONAU_CRYPTO_CsDonationUnitHelper *
+DONAU_CRYPTO_helper_cs_connect (
+  const struct GNUNET_CONFIGURATION_Handle *cfg,
+  const char *section,
+  DONAU_CRYPTO_CsDonationUnitKeyStatusCallback dkc,
+  void *dkc_cls);
+
+
+/**
+ * Function to call to 'poll' for updates to the available key material.
+ * Should be called whenever it is important that the key material status is
+ * current, like when handling a "/keys" request.  This function basically
+ * briefly checks if there are messages from the helper announcing changes to
+ * donation unit keys.
+ *
+ * @param dh helper process connection
+ */
+void
+DONAU_CRYPTO_helper_cs_poll (struct DONAU_CRYPTO_CsDonationUnitHelper *dh);
+
+
+/**
+ * Request helper @a dh to sign @a req.
+ *
+ * This operation will block until the signature has been obtained.  Should
+ * this process receive a signal (that is not ignored) while the operation is
+ * pending, the operation will fail.  Note that the helper may still believe
+ * that it created the signature. Thus, signals may result in a small
+ * differences in the signature counters.  Retrying in this case may work.
+ *
+ * @param dh helper process connection
+ * @param req information about the key to sign with and the value to sign
+ * @param for_melt true if for melt operation
+ * @param[out] bs set to the blind signature
+ * @return #TALER_EC_NONE on success
+ */
+enum TALER_ErrorCode
+DONAU_CRYPTO_helper_cs_sign (
+  struct DONAU_CRYPTO_CsDonationUnitHelper *dh,
+  const struct TALER_CRYPTO_CsSignRequest *req,
+  bool for_melt,
+  struct DONAU_BlindedDonationUnitSignature *bs);
+
+
+/**
+ * Request helper @a dh to sign batch of @a reqs requests.
+ *
+ * This operation will block until the signature has been obtained.  Should
+ * this process receive a signal (that is not ignored) while the operation is
+ * pending, the operation will fail.  Note that the helper may still believe
+ * that it created the signature. Thus, signals may result in a small
+ * differences in the signature counters.  Retrying in this case may work.
+ *
+ * @param dh helper process connection
+ * @param reqs information about the keys to sign with and the values to sign
+ * @param reqs_length length of the @a reqs array
+ * @param for_melt true if this is for a melt operation
+ * @param[out] bss array set to the blind signatures, must be of length @a 
reqs_length!
+ * @return #TALER_EC_NONE on success
+ */
+enum TALER_ErrorCode
+DONAU_CRYPTO_helper_cs_batch_sign (
+  struct DONAU_CRYPTO_CsDonationUnitHelper *dh,
+  unsigned int reqs_length,
+  const struct TALER_CRYPTO_CsSignRequest reqs[static reqs_length],
+  bool for_melt,
+  struct DONAU_BlindedDonationUnitSignature bss[static reqs_length]);
+
+
+/**
+ * Ask the helper to revoke the public key associated with @a h_cs.
+ * Will cause the helper to tell all clients that the key is now unavailable,
+ * and to create a replacement key.
+ *
+ * This operation will block until the revocation request has been
+ * transmitted.  Should this process receive a signal (that is not ignored)
+ * while the operation is pending, the operation may fail. If the key is
+ * unknown, this function will also appear to have succeeded. To be sure that
+ * the revocation worked, clients must watch the donation unit key status
+ * callback.
+ *
+ * @param dh helper to process connection
+ * @param h_cs hash of the CS public key to revoke
+ */
+void
+DONAU_CRYPTO_helper_cs_revoke (
+  struct DONAU_CRYPTO_CsDonationUnitHelper *dh,
+  const struct TALER_CsPubHashP *h_cs);
+
+
+/**
+ * Ask the helper to derive R using the information
+ * from @a cdr.
+ *
+ * This operation will block until the R has been obtained.  Should
+ * this process receive a signal (that is not ignored) while the operation is
+ * pending, the operation will fail.  Note that the helper may still believe
+ * that it created the signature. Thus, signals may result in a small
+ * differences in the signature counters.  Retrying in this case may work.
+ *
+ * @param dh helper to process connection
+ * @param cdr derivation input data
+ * @param for_melt true if this is for a melt operation
+ * @param[out] crp set to the pair of R values
+ * @return set to the error code (or #TALER_EC_NONE on success)
+ */
+enum TALER_ErrorCode
+DONAU_CRYPTO_helper_cs_r_derive (
+  struct DONAU_CRYPTO_CsDonationUnitHelper *dh,
+  const struct TALER_CRYPTO_CsDeriveRequest *cdr,
+  bool for_melt,
+  struct GNUNET_CRYPTO_CSPublicRPairP *crp);
+
+
+/**
+ * Ask the helper to derive R using the information from @a cdrs.
+ *
+ * This operation will block until the R has been obtained.  Should
+ * this process receive a signal (that is not ignored) while the operation is
+ * pending, the operation will fail.  Note that the helper may still believe
+ * that it created the signature. Thus, signals may result in a small
+ * differences in the signature counters.  Retrying in this case may work.
+ *
+ * @param dh helper to process connection
+ * @param cdrs_length length of the @a cdrs array
+ * @param cdrs array with derivation input data
+ * @param for_melt true if this is for a melt operation
+ * @param[out] crps array set to the pair of R values, must be of length @a 
cdrs_length
+ * @return set to the error code (or #TALER_EC_NONE on success)
+ */
+enum TALER_ErrorCode
+DONAU_CRYPTO_helper_cs_r_batch_derive (
+  struct DONAU_CRYPTO_CsDonationUnitHelper *dh,
+  unsigned int cdrs_length,
+  const struct TALER_CRYPTO_CsDeriveRequest cdrs[static cdrs_length],
+  bool for_melt,
+  struct GNUNET_CRYPTO_CSPublicRPairP crps[static cdrs_length]);
+
+
+/**
+ * Close connection to @a dh.
+ *
+ * @param[in] dh connection to close
+ */
+void
+DONAU_CRYPTO_helper_cs_disconnect (
+  struct DONAU_CRYPTO_CsDonationUnitHelper *dh);
+
+
+/*********************** Helper-based EDDSA operations 
*****************************/
+
+/**
+ * Handle for talking to an online key signing helper.
+ */
+struct DONAU_CRYPTO_DonauSignHelper;
+
+/**
+ * Function called with information about available keys for signing.  Usually
+ * only called once per key upon connect. Also called again in case a key is
+ * being revoked, in that case with an @a end_time of zero.
+ *
+ * @param cls closure
+ * @param start_time when does the key become available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param validity_duration how long does the key remain available for signing;
+ *                 zero if the key has been revoked or purged
+ * @param exchange_pub the public key itself, NULL if the key was revoked or 
purged
+ * @param sm_pub public key of the security module, NULL if the key was 
revoked or purged
+ * @param sm_sig signature from the security module, NULL if the key was 
revoked or purged
+ *               The signature was already verified against @a sm_pub.
+ */
+typedef void
+(*DONAU_CRYPTO_DonauKeyStatusCallback)(
+  void *cls,
+  struct GNUNET_TIME_Timestamp start_time,
+  struct GNUNET_TIME_Relative validity_duration,
+  const struct DONAU_DonauPublicKeyP *exchange_pub,
+  const struct TALER_SecurityModulePublicKeyP *sm_pub,
+  const struct TALER_SecurityModuleSignatureP *sm_sig);
+
+
+/**
+ * Initiate connection to an online signing key helper.
+ *
+ * @param cfg configuration to use
+ * @param section configuration section prefix to use, usually 'taler' or 
'donau'
+ * @param ekc function to call with key information
+ * @param ekc_cls closure for @a ekc
+ * @return NULL on error (such as bad @a cfg).
+ */
+struct DONAU_CRYPTO_DonauSignHelper *
+DONAU_CRYPTO_helper_esign_connect (
+  const struct GNUNET_CONFIGURATION_Handle *cfg,
+  const char *section,
+  DONAU_CRYPTO_DonauKeyStatusCallback ekc,
+  void *ekc_cls);
+
+
+/**
+ * Function to call to 'poll' for updates to the available key material.
+ * Should be called whenever it is important that the key material status is
+ * current, like when handling a "/keys" request.  This function basically
+ * briefly checks if there are messages from the helper announcing changes to
+ * donau online signing keys.
+ *
+ * @param esh helper process connection
+ */
+void
+DONAU_CRYPTO_helper_esign_poll (struct DONAU_CRYPTO_DonauSignHelper *esh);
+
+
+/**
+ * Request helper @a esh to sign @a msg using the current online
+ * signing key.
+ *
+ * This operation will block until the signature has been obtained.  Should
+ * this process receive a signal (that is not ignored) while the operation is
+ * pending, the operation will fail.  Note that the helper may still believe
+ * that it created the signature. Thus, signals may result in a small
+ * differences in the signature counters.  Retrying in this case may work.
+ *
+ * @param esh helper process connection
+ * @param purpose message to sign (must extend beyond the purpose)
+ * @param[out] donau_pub set to the public key used for the signature upon 
success
+ * @param[out] donau_sig set to the signature upon success
+ * @return the error code (or #TALER_EC_NONE on success)
+ */
+enum TALER_ErrorCode
+DONAU_CRYPTO_helper_esign_sign_ (
+  struct DONAU_CRYPTO_DonauSignHelper *esh,
+  const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
+  struct DONAU_DonauPublicKeyP *donau_pub,
+  struct DONAU_DonauSignatureP *donau_sig);
+
+
+/**
+ * Request helper @a esh to sign @a msg using the current online
+ * signing key.
+ *
+ * This operation will block until the signature has been obtained.  Should
+ * this process receive a signal (that is not ignored) while the operation is
+ * pending, the operation will fail.  Note that the helper may still believe
+ * that it created the signature. Thus, signals may result in a small
+ * differences in the signature counters.  Retrying in this case may work.
+ *
+ * @param esh helper process connection
+ * @param ps message to sign (MUST begin with a purpose)
+ * @param[out] epub set to the public key used for the signature upon success
+ * @param[out] esig set to the signature upon success
+ * @return the error code (or #TALER_EC_NONE on success)
+ */
+#define DONAU_CRYPTO_helper_esign_sign(esh,ps,epub,esig) (         \
+    /* check size is set correctly */                              \
+    GNUNET_assert (ntohl ((ps)->purpose.size) == sizeof (*ps)),    \
+    /* check 'ps' begins with the purpose */                       \
+    GNUNET_static_assert (((void*) (ps)) ==                        \
+                          ((void*) &(ps)->purpose)),               \
+    DONAU_CRYPTO_helper_esign_sign_ (esh,                          \
+                                     &(ps)->purpose,               \
+                                     epub,                         \
+                                     esig) )
+
+
+/**
+ * Ask the helper to revoke the public key @a donau_pub .
+ * Will cause the helper to tell all clients that the key is now unavailable,
+ * and to create a replacement key.
+ *
+ * This operation will block until the revocation request has been
+ * transmitted.  Should this process receive a signal (that is not ignored)
+ * while the operation is pending, the operation may fail. If the key is
+ * unknown, this function will also appear to have succeeded. To be sure that
+ * the revocation worked, clients must watch the signing key status callback.
+ *
+ * @param esh helper to process connection
+ * @param donau_pub the public key to revoke
+ */
+void
+DONAU_CRYPTO_helper_esign_revoke (
+  struct DONAU_CRYPTO_DonauSignHelper *esh,
+  const struct DONAU_DonauPublicKeyP *donau_pub);
+
+
+/**
+ * Close connection to @a esh.
+ *
+ * @param[in] esh connection to close
+ */
+void
+DONAU_CRYPTO_helper_esign_disconnect (
+  struct DONAU_CRYPTO_DonauSignHelper *esh);
+
+
 #endif
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index 92f00ac..889249e 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -15,7 +15,10 @@ lib_LTLIBRARIES = \
 libdonauutil_la_SOURCES = \
   charity_signatures.c \
   donau_signatures.c \
-  donau_os_installation.c 
+  donau_os_installation.c \
+  crypto_helper_cs.c \
+  crypto_helper_rsa.c \
+  crypto_helper_esign.c
 
 libdonauutil_la_LIBADD = \
   -ltalerutil \
diff --git a/src/util/crypto_helper_common.c b/src/util/crypto_helper_common.c
new file mode 100644
index 0000000..fccfe9b
--- /dev/null
+++ b/src/util/crypto_helper_common.c
@@ -0,0 +1,51 @@
+/*
+  This file is part of TALER
+  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
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file util/crypto_helper_common.c
+ * @brief Common functions for the exchange security modules
+ * @author Florian Dold <dold@taler.net>
+ */
+#include <taler/platform.h>
+#include <taler/taler_util.h>
+#include <taler/taler_signatures.h>
+
+
+enum GNUNET_GenericReturnValue
+TALER_crypto_helper_send_all (int sock,
+                              const void *buf,
+                              size_t buf_size)
+{
+  size_t off = 0;
+
+  while (off < buf_size)
+  {
+    ssize_t ret;
+
+    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/crypto_helper_common.h b/src/util/crypto_helper_common.h
new file mode 100644
index 0000000..cf8b281
--- /dev/null
+++ b/src/util/crypto_helper_common.h
@@ -0,0 +1,41 @@
+/*
+  This file is part of GNU Taler
+  Copyright (C) 2021 Taler Systems SA
+
+  GNU Taler is free software; you can redistribute it and/or modify it under 
the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file util/crypto_helper_common.h
+ * @brief Common functions for the exchange security modules
+ * @author Florian Dold <dold@taler.net>
+ */
+#ifndef CRYPTO_HELPER_COMMON_H
+#define CRYPTO_HELPER_COMMON_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_network_lib.h>
+
+/**
+ * Send all @a buf_size bytes from @a buf to @a sock.
+ *
+ * @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
+ */
+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_cs.c b/src/util/crypto_helper_cs.c
new file mode 100644
index 0000000..fd04663
--- /dev/null
+++ b/src/util/crypto_helper_cs.c
@@ -0,0 +1,1317 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2020-2024 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file util/crypto_helper_cs.c
+ * @brief utility functions for running out-of-process private key operations
+ * @author Christian Grothoff
+ * @author Pius Loosli
+ */
+#include "taler/platform.h"
+#include "taler/taler_util.h"
+#include "taler/taler_signatures.h"
+#include "taler/taler-exchange-secmod-cs.h"
+#include <poll.h>
+#include "crypto_helper_common.h"
+#include "donau_util.h"
+
+struct DONAU_CRYPTO_CsDonationUnitHelper
+{
+  /**
+   * Function to call with updates to available key material.
+   */
+  DONAU_CRYPTO_CsDonationUnitKeyStatusCallback dkc;
+
+  /**
+   * Closure for @e dkc
+   */
+  void *dkc_cls;
+
+  /**
+   * Socket address of the donation unit helper process.
+   * Used to reconnect if the connection breaks.
+   */
+  struct sockaddr_un sa;
+
+  /**
+   * The UNIX domain socket, -1 if we are currently not connected.
+   */
+  int sock;
+
+  /**
+   * Have we ever been sync'ed?
+   */
+  bool synced;
+};
+
+
+/**
+ * Disconnect from the helper process.  Updates
+ * @e sock field in @a dh.
+ *
+ * @param[in,out] dh handle to tear down connection of
+ */
+static void
+do_disconnect (struct DONAU_CRYPTO_CsDonationUnitHelper *dh)
+{
+  GNUNET_break (0 == close (dh->sock));
+  dh->sock = -1;
+  dh->synced = false;
+}
+
+
+/**
+ * Try to connect to the helper process.  Updates
+ * @e sock field in @a dh.
+ *
+ * @param[in,out] dh handle to establish connection for
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+try_connect (struct DONAU_CRYPTO_CsDonationUnitHelper *dh)
+{
+  if (-1 != dh->sock)
+    return GNUNET_OK;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Establishing connection!\n");
+  dh->sock = socket (AF_UNIX,
+                     SOCK_STREAM,
+                     0);
+  if (-1 == dh->sock)
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                         "socket");
+    return GNUNET_SYSERR;
+  }
+  if (0 !=
+      connect (dh->sock,
+               (const struct sockaddr *) &dh->sa,
+               sizeof (dh->sa)))
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+                              "connect",
+                              dh->sa.sun_path);
+    do_disconnect (dh);
+    return GNUNET_SYSERR;
+  }
+  DONAU_CRYPTO_helper_cs_poll (dh);
+  return GNUNET_OK;
+}
+
+
+struct DONAU_CRYPTO_CsDonationUnitHelper *
+DONAU_CRYPTO_helper_cs_connect (
+  const struct GNUNET_CONFIGURATION_Handle *cfg,
+  const char *section,
+  DONAU_CRYPTO_CsDonationUnitKeyStatusCallback dkc,
+  void *dkc_cls)
+{
+  struct DONAU_CRYPTO_CsDonationUnitHelper *dh;
+  char *unixpath;
+  char *secname;
+
+  GNUNET_asprintf (&secname,
+                   "%s-exchange-secmod-cs",
+                   section);
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                               secname,
+                                               "UNIXPATH",
+                                               &unixpath))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               secname,
+                               "UNIXPATH");
+    GNUNET_free (secname);
+    return NULL;
+  }
+  /* we use >= here because we want the sun_path to always
+     be 0-terminated */
+  if (strlen (unixpath) >= sizeof (dh->sa.sun_path))
+  {
+    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                               secname,
+                               "UNIXPATH",
+                               "path too long");
+    GNUNET_free (unixpath);
+    GNUNET_free (secname);
+    return NULL;
+  }
+  GNUNET_free (secname);
+  dh = GNUNET_new (struct DONAU_CRYPTO_CsDonationUnitHelper);
+  dh->dkc = dkc;
+  dh->dkc_cls = dkc_cls;
+  dh->sa.sun_family = AF_UNIX;
+  strncpy (dh->sa.sun_path,
+           unixpath,
+           sizeof (dh->sa.sun_path) - 1);
+  GNUNET_free (unixpath);
+  dh->sock = -1;
+  if (GNUNET_OK !=
+      try_connect (dh))
+  {
+    DONAU_CRYPTO_helper_cs_disconnect (dh);
+    return NULL;
+  }
+  return dh;
+}
+
+
+/**
+ * Handle a #DONAU_HELPER_CS_MT_AVAIL message from the helper.
+ *
+ * @param dh helper context
+ * @param hdr message that we received
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+handle_mt_avail (struct DONAU_CRYPTO_CsDonationUnitHelper *dh,
+                 const struct GNUNET_MessageHeader *hdr)
+{
+  const struct TALER_CRYPTO_CsKeyAvailableNotification *kan
+    = (const struct TALER_CRYPTO_CsKeyAvailableNotification *) hdr;
+  const char *buf = (const char *) &kan[1];
+  const char *section_name;
+  uint16_t snl;
+
+  if (sizeof (*kan) > ntohs (hdr->size))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  snl = ntohs (kan->section_name_len);
+  if (ntohs (hdr->size) != sizeof (*kan) + snl)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (0 == snl)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  section_name = buf;
+  if ('\0' != section_name[snl - 1])
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  {
+    struct GNUNET_CRYPTO_BlindSignPublicKey *bsign_pub;
+    struct TALER_CsPubHashP h_cs;
+
+    bsign_pub = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPublicKey);
+    bsign_pub->cipher = GNUNET_CRYPTO_BSA_CS;
+    bsign_pub->rc = 1;
+    bsign_pub->details.cs_public_key = kan->denom_pub;
+
+    GNUNET_CRYPTO_hash (&bsign_pub->details.cs_public_key,
+                        sizeof (bsign_pub->details.cs_public_key),
+                        &bsign_pub->pub_key_hash);
+    h_cs.hash = bsign_pub->pub_key_hash;
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Received CS key %s (%s)\n",
+                GNUNET_h2s (&h_cs.hash),
+                section_name);
+    if (GNUNET_OK !=
+        TALER_exchange_secmod_cs_verify (
+          &h_cs,
+          section_name,
+          GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
+          GNUNET_TIME_relative_ntoh (kan->duration_withdraw),
+          &kan->secm_pub,
+          &kan->secm_sig))
+    {
+      GNUNET_break_op (0);
+      GNUNET_CRYPTO_blind_sign_pub_decref (bsign_pub);
+      return GNUNET_SYSERR;
+    }
+    dh->dkc (dh->dkc_cls,
+             section_name,
+             GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
+             GNUNET_TIME_relative_ntoh (kan->duration_withdraw),
+             &h_cs,
+             bsign_pub,
+             &kan->secm_pub,
+             &kan->secm_sig);
+    GNUNET_CRYPTO_blind_sign_pub_decref (bsign_pub);
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Handle a #DONAU_HELPER_CS_MT_PURGE message from the helper.
+ *
+ * @param dh helper context
+ * @param hdr message that we received
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+handle_mt_purge (struct DONAU_CRYPTO_CsDonationUnitHelper *dh,
+                 const struct GNUNET_MessageHeader *hdr)
+{
+  const struct TALER_CRYPTO_CsKeyPurgeNotification *pn
+    = (const struct TALER_CRYPTO_CsKeyPurgeNotification *) hdr;
+
+  if (sizeof (*pn) != ntohs (hdr->size))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Received revocation of donation unit key %s\n",
+              GNUNET_h2s (&pn->h_cs.hash));
+  dh->dkc (dh->dkc_cls,
+           NULL,
+           GNUNET_TIME_UNIT_ZERO_TS,
+           GNUNET_TIME_UNIT_ZERO,
+           &pn->h_cs,
+           NULL,
+           NULL,
+           NULL);
+  return GNUNET_OK;
+}
+
+
+void
+DONAU_CRYPTO_helper_cs_poll (struct DONAU_CRYPTO_CsDonationUnitHelper *dh)
+{
+  char buf[UINT16_MAX];
+  size_t off = 0;
+  unsigned int retry_limit = 3;
+  const struct GNUNET_MessageHeader *hdr
+    = (const struct GNUNET_MessageHeader *) buf;
+
+  if (GNUNET_OK !=
+      try_connect (dh))
+    return; /* give up */
+  while (1)
+  {
+    uint16_t msize;
+    ssize_t ret;
+
+    ret = recv (dh->sock,
+                buf + off,
+                sizeof (buf) - off,
+                (dh->synced && (0 == off))
+                ? MSG_DONTWAIT
+                : 0);
+    if (ret < 0)
+    {
+      if (EINTR == errno)
+        continue;
+      if (EAGAIN == errno)
+      {
+        GNUNET_assert (dh->synced);
+        GNUNET_assert (0 == off);
+        break;
+      }
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                           "recv");
+      do_disconnect (dh);
+      if (0 == retry_limit)
+        return; /* give up */
+      if (GNUNET_OK !=
+          try_connect (dh))
+        return; /* give up */
+      retry_limit--;
+      continue;
+    }
+    if (0 == ret)
+    {
+      GNUNET_break (0 == off);
+      return;
+    }
+    off += ret;
+more:
+    if (off < sizeof (struct GNUNET_MessageHeader))
+      continue;
+    msize = ntohs (hdr->size);
+    if (off < msize)
+      continue;
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Received message of type %u and length %u\n",
+                (unsigned int) ntohs (hdr->type),
+                (unsigned int) msize);
+    switch (ntohs (hdr->type))
+    {
+    case TALER_HELPER_CS_MT_AVAIL:
+      if (GNUNET_OK !=
+          handle_mt_avail (dh,
+                           hdr))
+      {
+        GNUNET_break_op (0);
+        do_disconnect (dh);
+        return;
+      }
+      break;
+    case TALER_HELPER_CS_MT_PURGE:
+      if (GNUNET_OK !=
+          handle_mt_purge (dh,
+                           hdr))
+      {
+        GNUNET_break_op (0);
+        do_disconnect (dh);
+        return;
+      }
+      break;
+    case TALER_HELPER_CS_SYNCED:
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Now synchronized with CS helper\n");
+      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;
+  }
+}
+
+
+enum TALER_ErrorCode
+DONAU_CRYPTO_helper_cs_sign (
+  struct DONAU_CRYPTO_CsDonationUnitHelper *dh,
+  const struct TALER_CRYPTO_CsSignRequest *req,
+  bool for_melt,
+  struct DONAU_BlindedDonationUnitSignature *bs)
+{
+  enum TALER_ErrorCode ec = TALER_EC_INVALID;
+  const struct TALER_CsPubHashP *h_cs = req->h_cs;
+
+  memset (bs,
+          0,
+          sizeof (*bs));
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Starting signature process\n");
+  if (GNUNET_OK !=
+      try_connect (dh))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to connect to helper\n");
+    return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Requesting signature\n");
+  {
+    char buf[sizeof (struct TALER_CRYPTO_CsSignRequestMessage)];
+    struct TALER_CRYPTO_CsSignRequestMessage *sr
+      = (struct TALER_CRYPTO_CsSignRequestMessage *) buf;
+
+    sr->header.size = htons (sizeof (buf));
+    sr->header.type = htons (TALER_HELPER_CS_MT_REQ_SIGN);
+    sr->for_melt = htonl (for_melt ? 1 : 0);
+    sr->h_cs = *h_cs;
+    sr->message = *req->blinded_planchet;
+    if (GNUNET_OK !=
+        TALER_crypto_helper_send_all (dh->sock,
+                                      buf,
+                                      sizeof (buf)))
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                           "send");
+      do_disconnect (dh);
+      return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+    }
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Awaiting reply\n");
+  {
+    char buf[UINT16_MAX];
+    size_t off = 0;
+    const struct GNUNET_MessageHeader *hdr
+      = (const struct GNUNET_MessageHeader *) buf;
+    bool finished = false;
+
+    while (1)
+    {
+      uint16_t msize;
+      ssize_t ret;
+
+      ret = recv (dh->sock,
+                  &buf[off],
+                  sizeof (buf) - off,
+                  (finished && (0 == off))
+                  ? MSG_DONTWAIT
+                  : 0);
+      if (ret < 0)
+      {
+        if (EINTR == errno)
+          continue;
+        if (EAGAIN == errno)
+        {
+          GNUNET_assert (finished);
+          GNUNET_assert (0 == off);
+          return ec;
+        }
+        GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                             "recv");
+        do_disconnect (dh);
+        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 ec;
+      }
+      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_CS_MT_RES_SIGNATURE:
+        if (msize != sizeof (struct TALER_CRYPTO_SignResponse))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          goto end;
+        }
+        if (finished)
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          goto end;
+        }
+        {
+          const struct TALER_CRYPTO_SignResponse *sr =
+            (const struct TALER_CRYPTO_SignResponse *) buf;
+          struct GNUNET_CRYPTO_BlindedSignature *blinded_sig;
+
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Received signature\n");
+          ec = TALER_EC_NONE;
+          finished = true;
+          blinded_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature);
+          blinded_sig->cipher = GNUNET_CRYPTO_BSA_CS;
+          blinded_sig->rc = 1;
+          blinded_sig->details.blinded_cs_answer.b = ntohl (sr->b);
+          blinded_sig->details.blinded_cs_answer.s_scalar = sr->cs_answer;
+          bs->blinded_sig = blinded_sig;
+          break;
+        }
+      case TALER_HELPER_CS_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);
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Signing failed with status %d!\n",
+                      ec);
+          finished = true;
+          break;
+        }
+      case TALER_HELPER_CS_MT_AVAIL:
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Received new key!\n");
+        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_CS_MT_PURGE:
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Received revocation!\n");
+        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_CS_SYNCED:
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Synchronized add odd time with CS 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;
+        goto end;
+      }
+      memmove (buf,
+               &buf[msize],
+               off - msize);
+      off -= msize;
+      goto more;
+    } /* while(1) */
+end:
+    if (finished)
+//      DONAU_blinded_donation_unit_sig_free (bs);
+      return ec;
+  }
+}
+
+
+void
+DONAU_CRYPTO_helper_cs_revoke (
+  struct DONAU_CRYPTO_CsDonationUnitHelper *dh,
+  const struct TALER_CsPubHashP *h_cs)
+{
+  struct TALER_CRYPTO_CsRevokeRequest rr = {
+    .header.size = htons (sizeof (rr)),
+    .header.type = htons (TALER_HELPER_CS_MT_REQ_REVOKE),
+    .h_cs = *h_cs
+  };
+
+  if (GNUNET_OK !=
+      try_connect (dh))
+    return; /* give up */
+  if (GNUNET_OK !=
+      TALER_crypto_helper_send_all (dh->sock,
+                                    &rr,
+                                    sizeof (rr)))
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                         "send");
+    do_disconnect (dh);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Requested revocation of donation unit key %s\n",
+              GNUNET_h2s (&h_cs->hash));
+}
+
+
+enum TALER_ErrorCode
+DONAU_CRYPTO_helper_cs_r_derive (struct DONAU_CRYPTO_CsDonationUnitHelper *dh,
+                                 const struct TALER_CRYPTO_CsDeriveRequest 
*cdr,
+                                 bool for_melt,
+                                 struct GNUNET_CRYPTO_CSPublicRPairP *crp)
+{
+  enum TALER_ErrorCode ec = TALER_EC_INVALID;
+  const struct TALER_CsPubHashP *h_cs = cdr->h_cs;
+  const struct GNUNET_CRYPTO_CsSessionNonce *nonce = cdr->nonce;
+
+  memset (crp,
+          0,
+          sizeof (*crp));
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Starting R derivation process\n");
+  if (GNUNET_OK !=
+      try_connect (dh))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to connect to helper\n");
+    return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Requesting R\n");
+  {
+    struct TALER_CRYPTO_CsRDeriveRequest rdr = {
+      .header.size = htons (sizeof (rdr)),
+      .header.type = htons (TALER_HELPER_CS_MT_REQ_RDERIVE),
+      .for_melt = htonl (for_melt ? 1 : 0),
+      .h_cs = *h_cs,
+      .nonce = *nonce
+    };
+
+    if (GNUNET_OK !=
+        TALER_crypto_helper_send_all (dh->sock,
+                                      &rdr,
+                                      sizeof (rdr)))
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                           "send");
+      do_disconnect (dh);
+      return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+    }
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Awaiting reply\n");
+  {
+    char buf[UINT16_MAX];
+    size_t off = 0;
+    const struct GNUNET_MessageHeader *hdr
+      = (const struct GNUNET_MessageHeader *) buf;
+    bool finished = false;
+
+    while (1)
+    {
+      uint16_t msize;
+      ssize_t ret;
+
+      ret = recv (dh->sock,
+                  &buf[off],
+                  sizeof (buf) - off,
+                  (finished && (0 == off))
+                  ? MSG_DONTWAIT
+                  : 0);
+      if (ret < 0)
+      {
+        if (EINTR == errno)
+          continue;
+        if (EAGAIN == errno)
+        {
+          GNUNET_assert (finished);
+          GNUNET_assert (0 == off);
+          return ec;
+        }
+        GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                             "recv");
+        do_disconnect (dh);
+        return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+      }
+      if (0 == ret)
+      {
+        GNUNET_break (0 == off);
+        if (! finished)
+          return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
+        return ec;
+      }
+      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_CS_MT_RES_RDERIVE:
+        if (msize != sizeof (struct TALER_CRYPTO_RDeriveResponse))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+        }
+        if (finished)
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+        }
+        {
+          const struct TALER_CRYPTO_RDeriveResponse *rdr =
+            (const struct TALER_CRYPTO_RDeriveResponse *) buf;
+
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Received R\n");
+          finished = true;
+          ec = TALER_EC_NONE;
+          *crp = rdr->r_pub;
+          break;
+        }
+      case TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE:
+        if (msize != sizeof (struct TALER_CRYPTO_RDeriveFailure))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+        }
+        {
+          const struct TALER_CRYPTO_RDeriveFailure *rdf =
+            (const struct TALER_CRYPTO_RDeriveFailure *) buf;
+
+          ec = (enum TALER_ErrorCode) ntohl (rdf->ec);
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "R derivation failed!\n");
+          finished = true;
+          break;
+        }
+      case TALER_HELPER_CS_MT_AVAIL:
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Received new key!\n");
+        if (GNUNET_OK !=
+            handle_mt_avail (dh,
+                             hdr))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+        }
+        break; /* while(1) loop ensures we recvfrom() again */
+      case TALER_HELPER_CS_MT_PURGE:
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Received revocation!\n");
+        if (GNUNET_OK !=
+            handle_mt_purge (dh,
+                             hdr))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+        }
+        break; /* while(1) loop ensures we recvfrom() again */
+      case TALER_HELPER_CS_SYNCED:
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Synchronized add odd time with CS 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);
+        return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+      }
+      memmove (buf,
+               &buf[msize],
+               off - msize);
+      off -= msize;
+      goto more;
+    } /* while(1) */
+  }
+}
+
+
+enum TALER_ErrorCode
+DONAU_CRYPTO_helper_cs_batch_sign (
+  struct DONAU_CRYPTO_CsDonationUnitHelper *dh,
+  unsigned int reqs_length,
+  const struct TALER_CRYPTO_CsSignRequest reqs[static reqs_length],
+  bool for_melt,
+  struct DONAU_BlindedDonationUnitSignature bss[static reqs_length])
+{
+  enum TALER_ErrorCode ec = TALER_EC_INVALID;
+  unsigned int rpos;
+  unsigned int rend;
+  unsigned int wpos;
+
+  memset (bss,
+          0,
+          sizeof (*bss) * reqs_length);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Starting signature process\n");
+  if (GNUNET_OK !=
+      try_connect (dh))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to connect to helper\n");
+    return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Requesting %u signatures\n",
+              reqs_length);
+  rpos = 0;
+  rend = 0;
+  wpos = 0;
+  while (rpos < reqs_length)
+  {
+    unsigned int mlen = sizeof (struct TALER_CRYPTO_BatchSignRequest);
+
+    while ( (rend < reqs_length) &&
+            (mlen + sizeof (struct TALER_CRYPTO_CsSignRequestMessage)
+             < UINT16_MAX) )
+    {
+      mlen += sizeof (struct TALER_CRYPTO_CsSignRequestMessage);
+      rend++;
+    }
+    {
+      char obuf[mlen] GNUNET_ALIGN;
+      struct TALER_CRYPTO_BatchSignRequest *bsr
+        = (struct TALER_CRYPTO_BatchSignRequest *) obuf;
+      void *wbuf;
+
+      bsr->header.type = htons (TALER_HELPER_CS_MT_REQ_BATCH_SIGN);
+      bsr->header.size = htons (mlen);
+      bsr->batch_size = htonl (rend - rpos);
+      wbuf = &bsr[1];
+      for (unsigned int i = rpos; i<rend; i++)
+      {
+        struct TALER_CRYPTO_CsSignRequestMessage *csm = wbuf;
+        const struct TALER_CRYPTO_CsSignRequest *csr = &reqs[i];
+
+        csm->header.size = htons (sizeof (*csm));
+        csm->header.type = htons (TALER_HELPER_CS_MT_REQ_SIGN);
+        csm->for_melt = htonl (for_melt ? 1 : 0);
+        csm->h_cs = *csr->h_cs;
+        csm->message = *csr->blinded_planchet;
+        wbuf += sizeof (*csm);
+      }
+      GNUNET_assert (wbuf == &obuf[mlen]);
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Sending batch request [%u-%u)\n",
+                  rpos,
+                  rend);
+      if (GNUNET_OK !=
+          TALER_crypto_helper_send_all (dh->sock,
+                                        obuf,
+                                        sizeof (obuf)))
+      {
+        GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                             "send");
+        do_disconnect (dh);
+        return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+      }
+    } /* end of obuf scope */
+    rpos = rend;
+    {
+      char buf[UINT16_MAX];
+      size_t off = 0;
+      const struct GNUNET_MessageHeader *hdr
+        = (const struct GNUNET_MessageHeader *) buf;
+      bool finished = false;
+
+      while (1)
+      {
+        uint16_t msize;
+        ssize_t ret;
+
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Awaiting reply at %u (up to %u)\n",
+                    wpos,
+                    rend);
+        ret = recv (dh->sock,
+                    &buf[off],
+                    sizeof (buf) - off,
+                    (finished && (0 == off))
+                  ? MSG_DONTWAIT
+                  : 0);
+        if (ret < 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 (dh);
+          return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+        }
+        if (0 == ret)
+        {
+          GNUNET_break (0 == off);
+          if (! finished)
+            return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
+          if (TALER_EC_NONE == ec)
+            break;
+          return ec;
+        }
+        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_CS_MT_RES_SIGNATURE:
+          if (msize != sizeof (struct TALER_CRYPTO_SignResponse))
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          }
+          if (finished)
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          }
+          {
+            const struct TALER_CRYPTO_SignResponse *sr =
+              (const struct TALER_CRYPTO_SignResponse *) buf;
+            struct GNUNET_CRYPTO_BlindedSignature *blinded_sig;
+            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                        "Received %u signature\n",
+                        wpos);
+            blinded_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature);
+            blinded_sig->cipher = GNUNET_CRYPTO_BSA_CS;
+            blinded_sig->rc = 1;
+            blinded_sig->details.blinded_cs_answer.b = ntohl (sr->b);
+            blinded_sig->details.blinded_cs_answer.s_scalar = sr->cs_answer;
+
+            bss[wpos].blinded_sig = blinded_sig;
+            wpos++;
+            if (wpos == rend)
+            {
+              if (TALER_EC_INVALID == ec)
+                ec = TALER_EC_NONE;
+              finished = true;
+            }
+            break;
+          }
+
+        case TALER_HELPER_CS_MT_RES_SIGN_FAILURE:
+          if (msize != sizeof (struct TALER_CRYPTO_SignFailure))
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          }
+          {
+            const struct TALER_CRYPTO_SignFailure *sf =
+              (const struct TALER_CRYPTO_SignFailure *) buf;
+
+            ec = (enum TALER_ErrorCode) ntohl (sf->ec);
+            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                        "Signing %u failed with status %d!\n",
+                        wpos,
+                        ec);
+            wpos++;
+            if (wpos == rend)
+            {
+              finished = true;
+            }
+            break;
+          }
+        case TALER_HELPER_CS_MT_AVAIL:
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Received new key!\n");
+          if (GNUNET_OK !=
+              handle_mt_avail (dh,
+                               hdr))
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          }
+          break; /* while(1) loop ensures we recvfrom() again */
+        case TALER_HELPER_CS_MT_PURGE:
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Received revocation!\n");
+          if (GNUNET_OK !=
+              handle_mt_purge (dh,
+                               hdr))
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          }
+          break; /* while(1) loop ensures we recvfrom() again */
+        case TALER_HELPER_CS_SYNCED:
+          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                      "Synchronized add odd time with CS 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);
+          return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+        }
+        memmove (buf,
+                 &buf[msize],
+                 off - msize);
+        off -= msize;
+        goto more;
+      } /* while(1) */
+    } /* scope */
+  } /* while (rpos < cdrs_length) */
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Existing with %u signatures and status %d\n",
+              wpos,
+              ec);
+  return ec;
+}
+
+
+enum TALER_ErrorCode
+DONAU_CRYPTO_helper_cs_r_batch_derive (
+  struct DONAU_CRYPTO_CsDonationUnitHelper *dh,
+  unsigned int cdrs_length,
+  const struct TALER_CRYPTO_CsDeriveRequest cdrs[static cdrs_length],
+  bool for_melt,
+  struct GNUNET_CRYPTO_CSPublicRPairP crps[static cdrs_length])
+{
+  enum TALER_ErrorCode ec = TALER_EC_INVALID;
+  unsigned int rpos;
+  unsigned int rend;
+  unsigned int wpos;
+
+  memset (crps,
+          0,
+          sizeof (*crps) * cdrs_length);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Starting R derivation process\n");
+  if (GNUNET_OK !=
+      try_connect (dh))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to connect to helper\n");
+    return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Requesting %u R pairs\n",
+              cdrs_length);
+  rpos = 0;
+  rend = 0;
+  wpos = 0;
+  while (rpos < cdrs_length)
+  {
+    unsigned int mlen = sizeof (struct TALER_CRYPTO_BatchDeriveRequest);
+
+    while ( (rend < cdrs_length) &&
+            (mlen + sizeof (struct TALER_CRYPTO_CsRDeriveRequest)
+             < UINT16_MAX) )
+    {
+      mlen += sizeof (struct TALER_CRYPTO_CsRDeriveRequest);
+      rend++;
+    }
+    {
+      char obuf[mlen] GNUNET_ALIGN;
+      struct TALER_CRYPTO_BatchDeriveRequest *bdr
+        = (struct TALER_CRYPTO_BatchDeriveRequest *) obuf;
+      void *wbuf;
+
+      bdr->header.type = htons (TALER_HELPER_CS_MT_REQ_BATCH_RDERIVE);
+      bdr->header.size = htons (mlen);
+      bdr->batch_size = htonl (rend - rpos);
+      wbuf = &bdr[1];
+      for (unsigned int i = rpos; i<rend; i++)
+      {
+        struct TALER_CRYPTO_CsRDeriveRequest *rdr = wbuf;
+        const struct TALER_CRYPTO_CsDeriveRequest *cdr = &cdrs[i];
+
+        rdr->header.size = htons (sizeof (*rdr));
+        rdr->header.type = htons (TALER_HELPER_CS_MT_REQ_RDERIVE);
+        rdr->for_melt = htonl (for_melt ? 1 : 0);
+        rdr->h_cs = *cdr->h_cs;
+        rdr->nonce = *cdr->nonce;
+        wbuf += sizeof (*rdr);
+      }
+      GNUNET_assert (wbuf == &obuf[mlen]);
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Sending batch request [%u-%u)\n",
+                  rpos,
+                  rend);
+      if (GNUNET_OK !=
+          TALER_crypto_helper_send_all (dh->sock,
+                                        obuf,
+                                        sizeof (obuf)))
+      {
+        GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                             "send");
+        do_disconnect (dh);
+        return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+      }
+    } /* end of obuf scope */
+    rpos = rend;
+    {
+      char buf[UINT16_MAX];
+      size_t off = 0;
+      const struct GNUNET_MessageHeader *hdr
+        = (const struct GNUNET_MessageHeader *) buf;
+      bool finished = false;
+
+      while (1)
+      {
+        uint16_t msize;
+        ssize_t ret;
+
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Awaiting reply at %u (up to %u)\n",
+                    wpos,
+                    rend);
+        ret = recv (dh->sock,
+                    &buf[off],
+                    sizeof (buf) - off,
+                    (finished && (0 == off))
+                  ? MSG_DONTWAIT
+                  : 0);
+        if (ret < 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 (dh);
+          return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+        }
+        if (0 == ret)
+        {
+          GNUNET_break (0 == off);
+          if (! finished)
+            return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
+          if (TALER_EC_NONE == ec)
+            break;
+          return ec;
+        }
+        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_CS_MT_RES_RDERIVE:
+          if (msize != sizeof (struct TALER_CRYPTO_RDeriveResponse))
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          }
+          if (finished)
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          }
+          {
+            const struct TALER_CRYPTO_RDeriveResponse *rdr =
+              (const struct TALER_CRYPTO_RDeriveResponse *) buf;
+
+            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                        "Received %u R pair\n",
+                        wpos);
+            crps[wpos] = rdr->r_pub;
+            wpos++;
+            if (wpos == rend)
+            {
+              if (TALER_EC_INVALID == ec)
+                ec = TALER_EC_NONE;
+              finished = true;
+            }
+            break;
+          }
+        case TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE:
+          if (msize != sizeof (struct TALER_CRYPTO_RDeriveFailure))
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          }
+          {
+            const struct TALER_CRYPTO_RDeriveFailure *rdf =
+              (const struct TALER_CRYPTO_RDeriveFailure *) buf;
+
+            ec = (enum TALER_ErrorCode) ntohl (rdf->ec);
+            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                        "R derivation %u failed with status %d!\n",
+                        wpos,
+                        ec);
+            wpos++;
+            if (wpos == rend)
+            {
+              finished = true;
+            }
+            break;
+          }
+        case TALER_HELPER_CS_MT_AVAIL:
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Received new key!\n");
+          if (GNUNET_OK !=
+              handle_mt_avail (dh,
+                               hdr))
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          }
+          break; /* while(1) loop ensures we recvfrom() again */
+        case TALER_HELPER_CS_MT_PURGE:
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Received revocation!\n");
+          if (GNUNET_OK !=
+              handle_mt_purge (dh,
+                               hdr))
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          }
+          break; /* while(1) loop ensures we recvfrom() again */
+        case TALER_HELPER_CS_SYNCED:
+          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                      "Synchronized add odd time with CS 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);
+          return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+        }
+        memmove (buf,
+                 &buf[msize],
+                 off - msize);
+        off -= msize;
+        goto more;
+      } /* while(1) */
+    } /* scope */
+  } /* while (rpos < cdrs_length) */
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Existing with %u signatures and status %d\n",
+              wpos,
+              ec);
+  return ec;
+}
+
+
+void
+DONAU_CRYPTO_helper_cs_disconnect (
+  struct DONAU_CRYPTO_CsDonationUnitHelper *dh)
+{
+  if (-1 != dh->sock)
+    do_disconnect (dh);
+  GNUNET_free (dh);
+}
+
+
+/* end of crypto_helper_cs.c */
diff --git a/src/util/crypto_helper_esign.c b/src/util/crypto_helper_esign.c
new file mode 100644
index 0000000..a4a4a40
--- /dev/null
+++ b/src/util/crypto_helper_esign.c
@@ -0,0 +1,557 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2020-2024 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file util/crypto_helper_esign.c
+ * @brief utility functions for running out-of-process private key operations
+ * @author Christian Grothoff
+ * @author Pius Loosli
+ */
+#include <taler/platform.h>
+#include <taler/taler_util.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler-exchange-secmod-eddsa.h>
+#include <poll.h>
+#include <crypto_helper_common.h>
+#include "donau_util.h"
+
+struct DONAU_CRYPTO_DonauSignHelper
+{
+  /**
+   * Function to call with updates to available key material.
+   */
+  DONAU_CRYPTO_DonauKeyStatusCallback dkc;
+
+  /**
+   * Closure for @e dkc
+   */
+  void *dkc_cls;
+
+  /**
+   * Socket address of the denomination helper process.
+   * Used to reconnect if the connection breaks.
+   */
+  struct sockaddr_un sa;
+
+  /**
+   * The UNIX domain socket, -1 if we are currently not connected.
+   */
+  int sock;
+
+  /**
+   * Have we reached the sync'ed state?
+   */
+  bool synced;
+
+};
+
+
+/**
+ * Disconnect from the helper process.  Updates
+ * @e sock field in @a dsh.
+ *
+ * @param[in,out] dsh handle to tear down connection of
+ */
+static void
+do_disconnect (struct DONAU_CRYPTO_DonauSignHelper *dsh)
+{
+  GNUNET_break (0 == close (dsh->sock));
+  dsh->sock = -1;
+  dsh->synced = false;
+}
+
+
+/**
+ * Try to connect to the helper process.  Updates
+ * @e sock field in @a dsh.
+ *
+ * @param[in,out] dsh handle to establish connection for
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+try_connect (struct DONAU_CRYPTO_DonauSignHelper *dsh)
+{
+  if (-1 != dsh->sock)
+    return GNUNET_OK;
+  dsh->sock = socket (AF_UNIX,
+                      SOCK_STREAM,
+                      0);
+  if (-1 == dsh->sock)
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                         "socket");
+    return GNUNET_SYSERR;
+  }
+  if (0 !=
+      connect (dsh->sock,
+               (const struct sockaddr *) &dsh->sa,
+               sizeof (dsh->sa)))
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+                              "connect",
+                              dsh->sa.sun_path);
+    do_disconnect (dsh);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+struct DONAU_CRYPTO_DonauSignHelper *
+DONAU_CRYPTO_helper_esign_connect (
+  const struct GNUNET_CONFIGURATION_Handle *cfg,
+  const char *section,
+  DONAU_CRYPTO_DonauKeyStatusCallback dkc,
+  void *dkc_cls)
+{
+  struct DONAU_CRYPTO_DonauSignHelper *dsh;
+  char *unixpath;
+  char *secname;
+
+  GNUNET_asprintf (&secname,
+                   "%s-exchange-secmod-eddsa",
+                   section);
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                               secname,
+                                               "UNIXPATH",
+                                               &unixpath))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               secname,
+                               "UNIXPATH");
+    GNUNET_free (secname);
+    return NULL;
+  }
+  /* we use >= here because we want the sun_path to always
+     be 0-terminated */
+  if (strlen (unixpath) >= sizeof (dsh->sa.sun_path))
+  {
+    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                               secname,
+                               "UNIXPATH",
+                               "path too long");
+    GNUNET_free (unixpath);
+    GNUNET_free (secname);
+    return NULL;
+  }
+  GNUNET_free (secname);
+  dsh = GNUNET_new (struct DONAU_CRYPTO_DonauSignHelper);
+  dsh->dkc = dkc;
+  dsh->dkc_cls = dkc_cls;
+  dsh->sa.sun_family = AF_UNIX;
+  strncpy (dsh->sa.sun_path,
+           unixpath,
+           sizeof (dsh->sa.sun_path) - 1);
+  GNUNET_free (unixpath);
+  dsh->sock = -1;
+  if (GNUNET_OK !=
+      try_connect (dsh))
+  {
+    DONAU_CRYPTO_helper_esign_disconnect (dsh);
+    return NULL;
+  }
+
+//  DONAU_CRYPTO_helper_esign_poll (dsh);
+  return dsh;
+}
+
+
+/**
+ * Handle a #TALER_HELPER_EDDSA_MT_AVAIL message from the helper.
+ *
+ * @param dsh helper context
+ * @param hdr message that we received
+ * @return #GNUNET_OK on success
+ */
+// static enum GNUNET_GenericReturnValue
+// handle_mt_avail (struct DONAU_CRYPTO_DonauSignHelper *dsh,
+//                 const struct GNUNET_MessageHeader *hdr)
+// {
+//  const struct TALER_CRYPTO_EddsaKeyAvailableNotification *kan
+//    = (const struct TALER_CRYPTO_EddsaKeyAvailableNotification *) hdr;
+//
+//  if (sizeof (*kan) != ntohs (hdr->size))
+//  {
+//    GNUNET_break_op (0);
+//    return GNUNET_SYSERR;
+//  }
+//
+//  if (GNUNET_OK !=
+//      TALER_exchange_secmod_eddsa_verify (
+//        &kan->exchange_pub,
+//        GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
+//        GNUNET_TIME_relative_ntoh (kan->duration),
+//        &kan->secm_pub,
+//        &kan->secm_sig))
+//  {
+//    GNUNET_break_op (0);
+//    return GNUNET_SYSERR;
+//  }
+//  dsh->dkc (dsh->dkc_cls,
+//            GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
+//            GNUNET_TIME_relative_ntoh (kan->duration),
+//            &kan->donau_pub,
+//            &kan->secm_pub,
+//            &kan->secm_sig);
+//  return GNUNET_OK;
+// }
+
+
+/**
+ * Handle a #TALER_HELPER_EDDSA_MT_PURGE message from the helper.
+ *
+ * @param dsh helper context
+ * @param hdr message that we received
+ * @return #GNUNET_OK on success
+ */
+// static enum GNUNET_GenericReturnValue
+// handle_mt_purge (struct DONAU_CRYPTO_DonauSignHelper *dsh,
+//                 const struct GNUNET_MessageHeader *hdr)
+// {
+//  const struct TALER_CRYPTO_EddsaKeyPurgeNotification *pn
+//    = (const struct TALER_CRYPTO_EddsaKeyPurgeNotification *) hdr;
+//
+//  if (sizeof (*pn) != ntohs (hdr->size))
+//  {
+//    GNUNET_break_op (0);
+//    return GNUNET_SYSERR;
+//  }
+//  dsh->dkc (dsh->dkc_cls,
+//            GNUNET_TIME_UNIT_ZERO_TS,
+//            GNUNET_TIME_UNIT_ZERO,
+//            &pn->exchange_pub,
+//            NULL,
+//            NULL);
+//  return GNUNET_OK;
+// }
+
+
+// void
+// DONAU_CRYPTO_helper_esign_poll (struct DONAU_CRYPTO_DonauSignHelper *dsh)
+// {
+//  char buf[UINT16_MAX];
+//  size_t off = 0;
+//  unsigned int retry_limit = 3;
+//  const struct GNUNET_MessageHeader *hdr
+//    = (const struct GNUNET_MessageHeader *) buf;
+//
+//  if (GNUNET_OK !=
+//      try_connect (dsh))
+//    return; /* give up */
+//  while (1)
+//  {
+//    uint16_t msize;
+//    ssize_t ret;
+//
+//    ret = recv (dsh->sock,
+//                buf + off,
+//                sizeof (buf) - off,
+//                (dsh->synced && (0 == off))
+//                ? MSG_DONTWAIT
+//                : 0);
+//    if (ret < 0)
+//    {
+//      if (EINTR == errno)
+//        continue;
+//      if (EAGAIN == errno)
+//      {
+//        GNUNET_assert (dsh->synced);
+//        GNUNET_assert (0 == off);
+//        break;
+//      }
+//      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+//                           "recv");
+//      do_disconnect (dsh);
+//      if (0 == retry_limit)
+//        return; /* give up */
+//      if (GNUNET_OK !=
+//          try_connect (dsh))
+//        return; /* give up */
+//      retry_limit--;
+//      continue;
+//    }
+//    if (0 == ret)
+//    {
+//      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:
+//      if (GNUNET_OK !=
+//          handle_mt_avail (dsh,
+//                           hdr))
+//      {
+//        GNUNET_break_op (0);
+//        do_disconnect (dsh);
+//        return;
+//      }
+//      break;
+//    case TALER_HELPER_EDDSA_MT_PURGE:
+//      if (GNUNET_OK !=
+//          handle_mt_purge (dsh,
+//                           hdr))
+//      {
+//        GNUNET_break_op (0);
+//        do_disconnect (dsh);
+//        return;
+//      }
+//      break;
+//    case TALER_HELPER_EDDSA_SYNCED:
+//      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+//                  "Now synchronized with EdDSA helper\n");
+//      dsh->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 (dsh);
+//      return;
+//    }
+//    memmove (buf,
+//             &buf[msize],
+//             off - msize);
+//    off -= msize;
+//    goto more;
+//  }
+// }
+
+
+// enum TALER_ErrorCode
+// DONAU_CRYPTO_helper_esign_sign_ (
+//  struct DONAU_CRYPTO_DonauSignHelper *dsh,
+//  const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
+//  struct DONAU_DonauPublicKeyP *exchange_pub,
+//  struct DONAU_DonauSignatureP *exchange_sig)
+// {
+//  uint32_t purpose_size = ntohl (purpose->size);
+//
+//  if (GNUNET_OK !=
+//      try_connect (dsh))
+//  {
+//    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+//                "Failed to connect to helper\n");
+//    return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
+//  }
+//  GNUNET_assert (purpose_size <
+//                 UINT16_MAX - sizeof (struct TALER_CRYPTO_EddsaSignRequest));
+//  {
+//    char buf[sizeof (struct TALER_CRYPTO_EddsaSignRequest) + purpose_size
+//             - sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose)];
+//    struct TALER_CRYPTO_EddsaSignRequest *sr
+//      = (struct TALER_CRYPTO_EddsaSignRequest *) buf;
+//
+//    sr->header.size = htons (sizeof (buf));
+//    sr->header.type = htons (TALER_HELPER_EDDSA_MT_REQ_SIGN);
+//    sr->reserved = htonl (0);
+//    GNUNET_memcpy (&sr->purpose,
+//                   purpose,
+//                   purpose_size);
+//    if (GNUNET_OK !=
+//        TALER_crypto_helper_send_all (dsh->sock,
+//                                      buf,
+//                                      sizeof (buf)))
+//    {
+//      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+//                                "send",
+//                                dsh->sa.sun_path);
+//      do_disconnect (dsh);
+//      return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
+//    }
+//  }
+//
+//  {
+//    char buf[UINT16_MAX];
+//    size_t off = 0;
+//    const struct GNUNET_MessageHeader *hdr
+//      = (const struct GNUNET_MessageHeader *) buf;
+//    bool finished = false;
+//    enum TALER_ErrorCode ec = TALER_EC_INVALID;
+//
+//    while (1)
+//    {
+//      ssize_t ret;
+//      uint16_t msize;
+//
+//      ret = recv (dsh->sock,
+//                  &buf[off],
+//                  sizeof (buf) - off,
+//                  (finished && (0 == off))
+//                  ? MSG_DONTWAIT
+//                  : 0);
+//      if (ret < 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 (dsh);
+//        return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
+//      }
+//      if (0 == ret)
+//      {
+//        GNUNET_break (0 == off);
+//        if (finished)
+//          return TALER_EC_NONE;
+//        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))
+//      {
+//      case TALER_HELPER_EDDSA_MT_RES_SIGNATURE:
+//        if (msize != sizeof (struct TALER_CRYPTO_EddsaSignResponse))
+//        {
+//          GNUNET_break_op (0);
+//          do_disconnect (dsh);
+//          return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
+//        }
+//        if (finished)
+//        {
+//          GNUNET_break_op (0);
+//          do_disconnect (dsh);
+//          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 (dsh);
+//          return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
+//        }
+//        {
+//          const struct TALER_CRYPTO_EddsaSignFailure *sf =
+//            (const struct TALER_CRYPTO_EddsaSignFailure *) buf;
+//
+//          finished = true;
+//          ec = (enum TALER_ErrorCode) ntohl (sf->ec);
+//          break;
+//        }
+//      case TALER_HELPER_EDDSA_MT_AVAIL:
+//        if (GNUNET_OK !=
+//            handle_mt_avail (dsh,
+//                             hdr))
+//        {
+//          GNUNET_break_op (0);
+//          do_disconnect (dsh);
+//          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 (dsh,
+//                             hdr))
+//        {
+//          GNUNET_break_op (0);
+//          do_disconnect (dsh);
+//          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");
+//        dsh->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 (dsh);
+//        return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
+//      }
+//      memmove (buf,
+//               &buf[msize],
+//               off - msize);
+//      off -= msize;
+//      goto more;
+//    } /* while(1) */
+//    return ec;
+//  }
+// }
+
+
+// void
+// DONAU_CRYPTO_helper_esign_revoke (
+//  struct DONAU_CRYPTO_DonauSignHelper *dsh,
+//  const struct DONAU_DonauPublicKeyP *exchange_pub)
+// {
+//  if (GNUNET_OK !=
+//      try_connect (dsh))
+//    return; /* give up */
+//  {
+//    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 (dsh->sock,
+//                                      &rr,
+//                                      sizeof (rr)))
+//    {
+//      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+//                           "send");
+//      do_disconnect (dsh);
+//      return;
+//    }
+//  }
+// }
+
+
+void
+DONAU_CRYPTO_helper_esign_disconnect (
+  struct DONAU_CRYPTO_DonauSignHelper *dsh)
+{
+  if (-1 != dsh->sock)
+    do_disconnect (dsh);
+  GNUNET_free (dsh);
+}
+
+
+/* end of crypto_helper_esign.c */
diff --git a/src/util/crypto_helper_rsa.c b/src/util/crypto_helper_rsa.c
new file mode 100644
index 0000000..91edd3e
--- /dev/null
+++ b/src/util/crypto_helper_rsa.c
@@ -0,0 +1,917 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2020-2024 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file util/crypto_helper_rsa.c
+ * @brief utility functions for running out-of-process private key operations
+ * @author Christian Grothoff
+ * @author Pius Loosli
+ */
+#include <taler/platform.h>
+#include <taler/taler_util.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler-exchange-secmod-rsa.h>
+#include <poll.h>
+#include <crypto_helper_common.h>
+#include "donau_util.h"
+
+struct DONAU_CRYPTO_RsaDonationUnitHelper
+{
+  /**
+   * Function to call with updates to available key material.
+   */
+  DONAU_CRYPTO_RsaDonationUnitKeyStatusCallback dkc;
+
+  /**
+   * Closure for @e dkc
+   */
+  void *dkc_cls;
+
+  /**
+   * Socket address of the donation unitination helper process.
+   * Used to reconnect if the connection breaks.
+   */
+  struct sockaddr_un sa;
+
+  /**
+   * The UNIX domain socket, -1 if we are currently not connected.
+   */
+  int sock;
+
+  /**
+   * Have we ever been sync'ed?
+   */
+  bool synced;
+};
+
+
+/**
+ * Disconnect from the helper process.  Updates
+ * @e sock field in @a dh.
+ *
+ * @param[in,out] dh handle to tear down connection of
+ */
+static void
+do_disconnect (struct DONAU_CRYPTO_RsaDonationUnitHelper *dh)
+{
+  GNUNET_break (0 == close (dh->sock));
+  dh->sock = -1;
+  dh->synced = false;
+}
+
+
+/**
+ * Try to connect to the helper process.  Updates
+ * @e sock field in @a dh.
+ *
+ * @param[in,out] dh handle to establish connection for
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+try_connect (struct DONAU_CRYPTO_RsaDonationUnitHelper *dh)
+{
+  if (-1 != dh->sock)
+    return GNUNET_OK;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Establishing connection!\n");
+  dh->sock = socket (AF_UNIX,
+                     SOCK_STREAM,
+                     0);
+  if (-1 == dh->sock)
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                         "socket");
+    return GNUNET_SYSERR;
+  }
+  if (0 !=
+      connect (dh->sock,
+               (const struct sockaddr *) &dh->sa,
+               sizeof (dh->sa)))
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+                              "connect",
+                              dh->sa.sun_path);
+    do_disconnect (dh);
+    return GNUNET_SYSERR;
+  }
+  DONAU_CRYPTO_helper_rsa_poll (dh);
+  return GNUNET_OK;
+}
+
+
+struct DONAU_CRYPTO_RsaDonationUnitHelper *
+DONAU_CRYPTO_helper_rsa_connect (
+  const struct GNUNET_CONFIGURATION_Handle *cfg,
+  const char *section,
+  DONAU_CRYPTO_RsaDonationUnitKeyStatusCallback dkc,
+  void *dkc_cls)
+{
+  struct DONAU_CRYPTO_RsaDonationUnitHelper *dh;
+  char *unixpath;
+  char *secname;
+
+  GNUNET_asprintf (&secname,
+                   "%s-exchange-secmod-rsa",
+                   section);
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                               secname,
+                                               "UNIXPATH",
+                                               &unixpath))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               secname,
+                               "UNIXPATH");
+    GNUNET_free (secname);
+    return NULL;
+  }
+  /* we use >= here because we want the sun_path to always
+     be 0-terminated */
+  if (strlen (unixpath) >= sizeof (dh->sa.sun_path))
+  {
+    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                               "taler-exchange-secmod-rsa",
+                               "UNIXPATH",
+                               "path too long");
+    GNUNET_free (unixpath);
+    GNUNET_free (secname);
+    return NULL;
+  }
+  GNUNET_free (secname);
+  dh = GNUNET_new (struct DONAU_CRYPTO_RsaDonationUnitHelper);
+  dh->dkc = dkc;
+  dh->dkc_cls = dkc_cls;
+  dh->sa.sun_family = AF_UNIX;
+  strncpy (dh->sa.sun_path,
+           unixpath,
+           sizeof (dh->sa.sun_path) - 1);
+  GNUNET_free (unixpath);
+  dh->sock = -1;
+  if (GNUNET_OK !=
+      try_connect (dh))
+  {
+    DONAU_CRYPTO_helper_rsa_disconnect (dh);
+    return NULL;
+  }
+  return dh;
+}
+
+
+/**
+ * Handle a #TALER_HELPER_RSA_MT_AVAIL message from the helper.
+ *
+ * @param dh helper context
+ * @param hdr message that we received
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+handle_mt_avail (struct DONAU_CRYPTO_RsaDonationUnitHelper *dh,
+                 const struct GNUNET_MessageHeader *hdr)
+{
+  const struct TALER_CRYPTO_RsaKeyAvailableNotification *kan
+    = (const struct TALER_CRYPTO_RsaKeyAvailableNotification *) hdr;
+  const char *buf = (const char *) &kan[1];
+  const char *section_name;
+  uint16_t ps;
+  uint16_t snl;
+
+  if (sizeof (*kan) > ntohs (hdr->size))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  ps = ntohs (kan->pub_size);
+  snl = ntohs (kan->section_name_len);
+  if (ntohs (hdr->size) != sizeof (*kan) + ps + snl)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (0 == snl)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  section_name = &buf[ps];
+  if ('\0' != section_name[snl - 1])
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  {
+    struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub;
+    struct TALER_RsaPubHashP h_rsa;
+
+    bs_pub = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPublicKey);
+    bs_pub->cipher = GNUNET_CRYPTO_BSA_RSA;
+    bs_pub->details.rsa_public_key
+      = GNUNET_CRYPTO_rsa_public_key_decode (buf,
+                                             ntohs (kan->pub_size));
+    if (NULL == bs_pub->details.rsa_public_key)
+    {
+      GNUNET_break_op (0);
+      GNUNET_free (bs_pub);
+      return GNUNET_SYSERR;
+    }
+    bs_pub->rc = 1;
+    GNUNET_CRYPTO_rsa_public_key_hash (bs_pub->details.rsa_public_key,
+                                       &bs_pub->pub_key_hash);
+    h_rsa.hash = bs_pub->pub_key_hash;
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Received RSA key %s (%s)\n",
+                GNUNET_h2s (&bs_pub->pub_key_hash),
+                section_name);
+    if (GNUNET_OK !=
+        TALER_exchange_secmod_rsa_verify (
+          &h_rsa,
+          section_name,
+          GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
+          GNUNET_TIME_relative_ntoh (kan->duration_withdraw),
+          &kan->secm_pub,
+          &kan->secm_sig))
+    {
+      GNUNET_break_op (0);
+      GNUNET_CRYPTO_blind_sign_pub_decref (bs_pub);
+      return GNUNET_SYSERR;
+    }
+    dh->dkc (dh->dkc_cls,
+             section_name,
+             GNUNET_TIME_timestamp_ntoh (kan->anchor_time),
+             GNUNET_TIME_relative_ntoh (kan->duration_withdraw),
+             &h_rsa,
+             bs_pub,
+             &kan->secm_pub,
+             &kan->secm_sig);
+    GNUNET_CRYPTO_blind_sign_pub_decref (bs_pub);
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Handle a #TALER_HELPER_RSA_MT_PURGE message from the helper.
+ *
+ * @param dh helper context
+ * @param hdr message that we received
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+handle_mt_purge (struct DONAU_CRYPTO_RsaDonationUnitHelper *dh,
+                 const struct GNUNET_MessageHeader *hdr)
+{
+  const struct TALER_CRYPTO_RsaKeyPurgeNotification *pn
+    = (const struct TALER_CRYPTO_RsaKeyPurgeNotification *) hdr;
+
+  if (sizeof (*pn) != ntohs (hdr->size))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Received revocation of donation unit key %s\n",
+              GNUNET_h2s (&pn->h_rsa.hash));
+  dh->dkc (dh->dkc_cls,
+           NULL,
+           GNUNET_TIME_UNIT_ZERO_TS,
+           GNUNET_TIME_UNIT_ZERO,
+           &pn->h_rsa,
+           NULL,
+           NULL,
+           NULL);
+  return GNUNET_OK;
+}
+
+
+void
+DONAU_CRYPTO_helper_rsa_poll (struct DONAU_CRYPTO_RsaDonationUnitHelper *dh)
+{
+  char buf[UINT16_MAX];
+  size_t off = 0;
+  unsigned int retry_limit = 3;
+  const struct GNUNET_MessageHeader *hdr
+    = (const struct GNUNET_MessageHeader *) buf;
+
+  if (GNUNET_OK !=
+      try_connect (dh))
+    return; /* give up */
+  while (1)
+  {
+    uint16_t msize;
+    ssize_t ret;
+
+    ret = recv (dh->sock,
+                buf + off,
+                sizeof (buf) - off,
+                (dh->synced && (0 == off))
+                ? MSG_DONTWAIT
+                : 0);
+    if (ret < 0)
+    {
+      if (EINTR == errno)
+        continue;
+      if (EAGAIN == errno)
+      {
+        GNUNET_assert (dh->synced);
+        GNUNET_assert (0 == off);
+        break;
+      }
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                           "recv");
+      do_disconnect (dh);
+      if (0 == retry_limit)
+        return; /* give up */
+      if (GNUNET_OK !=
+          try_connect (dh))
+        return; /* give up */
+      retry_limit--;
+      continue;
+    }
+    if (0 == ret)
+    {
+      GNUNET_break (0 == off);
+      return;
+    }
+    off += ret;
+more:
+    if (off < sizeof (struct GNUNET_MessageHeader))
+      continue;
+    msize = ntohs (hdr->size);
+    if (off < msize)
+      continue;
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Received message of type %u and length %u\n",
+                (unsigned int) ntohs (hdr->type),
+                (unsigned int) msize);
+    switch (ntohs (hdr->type))
+    {
+    case TALER_HELPER_RSA_MT_AVAIL:
+      if (GNUNET_OK !=
+          handle_mt_avail (dh,
+                           hdr))
+      {
+        GNUNET_break_op (0);
+        do_disconnect (dh);
+        return;
+      }
+      break;
+    case TALER_HELPER_RSA_MT_PURGE:
+      if (GNUNET_OK !=
+          handle_mt_purge (dh,
+                           hdr))
+      {
+        GNUNET_break_op (0);
+        do_disconnect (dh);
+        return;
+      }
+      break;
+    case TALER_HELPER_RSA_SYNCED:
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Now synchronized with RSA helper\n");
+      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;
+  }
+}
+
+
+enum TALER_ErrorCode
+DONAU_CRYPTO_helper_rsa_sign (
+  struct DONAU_CRYPTO_RsaDonationUnitHelper *dh,
+  const struct TALER_CRYPTO_RsaSignRequest *rsr,
+  struct DONAU_BlindedDonationUnitSignature *bs)
+{
+  enum TALER_ErrorCode ec = TALER_EC_INVALID;
+
+  memset (bs,
+          0,
+          sizeof (*bs));
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Starting signature process\n");
+  if (GNUNET_OK !=
+      try_connect (dh))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to connect to helper\n");
+    return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Requesting signature\n");
+  {
+    char buf[sizeof (struct TALER_CRYPTO_SignRequest) + rsr->msg_size];
+    struct TALER_CRYPTO_SignRequest *sr
+      = (struct TALER_CRYPTO_SignRequest *) buf;
+
+    sr->header.size = htons (sizeof (buf));
+    sr->header.type = htons (TALER_HELPER_RSA_MT_REQ_SIGN);
+    sr->reserved = htonl (0);
+    sr->h_rsa = *rsr->h_rsa;
+    GNUNET_memcpy (&sr[1],
+                   rsr->msg,
+                   rsr->msg_size);
+    if (GNUNET_OK !=
+        TALER_crypto_helper_send_all (dh->sock,
+                                      buf,
+                                      sizeof (buf)))
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                           "send");
+      do_disconnect (dh);
+      return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+    }
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Awaiting reply\n");
+  {
+    char buf[UINT16_MAX];
+    size_t off = 0;
+    const struct GNUNET_MessageHeader *hdr
+      = (const struct GNUNET_MessageHeader *) buf;
+    bool finished = false;
+
+    while (1)
+    {
+      uint16_t msize;
+      ssize_t ret;
+
+      ret = recv (dh->sock,
+                  &buf[off],
+                  sizeof (buf) - off,
+                  (finished && (0 == off))
+                  ? MSG_DONTWAIT
+                  : 0);
+      if (ret < 0)
+      {
+        if (EINTR == errno)
+          continue;
+        if (EAGAIN == errno)
+        {
+          GNUNET_assert (finished);
+          GNUNET_assert (0 == off);
+          return ec;
+        }
+        GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                             "recv");
+        do_disconnect (dh);
+        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 ec;
+      }
+      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_RES_SIGNATURE:
+        if (msize < sizeof (struct TALER_CRYPTO_SignResponse))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          goto end;
+        }
+        if (finished)
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          goto end;
+        }
+        {
+          const struct TALER_CRYPTO_SignResponse *sr =
+            (const struct TALER_CRYPTO_SignResponse *) buf;
+          struct GNUNET_CRYPTO_RsaSignature *rsa_signature;
+          struct GNUNET_CRYPTO_BlindedSignature *blind_sig;
+
+          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;
+          }
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Received signature\n");
+          ec = TALER_EC_NONE;
+          finished = true;
+          blind_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature);
+          blind_sig->cipher = GNUNET_CRYPTO_BSA_RSA;
+          blind_sig->rc = 1;
+          blind_sig->details.blinded_rsa_signature = rsa_signature;
+          bs->blinded_sig = blind_sig;
+          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);
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Signing failed!\n");
+          finished = true;
+          break;
+        }
+      case TALER_HELPER_RSA_MT_AVAIL:
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Received new key!\n");
+        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:
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Received revocation!\n");
+        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;
+        goto end;
+      }
+      memmove (buf,
+               &buf[msize],
+               off - msize);
+      off -= msize;
+      goto more;
+    } /* while(1) */
+end:
+    if (finished)
+//      DONAU_blinded_donation_unit_sig_free(bs);
+      return ec;
+  }
+}
+
+
+enum TALER_ErrorCode
+DONAU_CRYPTO_helper_rsa_batch_sign (
+  struct DONAU_CRYPTO_RsaDonationUnitHelper *dh,
+  unsigned int rsrs_length,
+  const struct TALER_CRYPTO_RsaSignRequest rsrs[static rsrs_length],
+  struct DONAU_BlindedDonationUnitSignature bss[static rsrs_length])
+{
+  enum TALER_ErrorCode ec = TALER_EC_INVALID;
+  unsigned int rpos;
+  unsigned int rend;
+  unsigned int wpos;
+
+  memset (bss,
+          0,
+          sizeof (*bss) * rsrs_length);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Starting signature process\n");
+  if (GNUNET_OK !=
+      try_connect (dh))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to connect to helper\n");
+    return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Requesting %u signatures\n",
+              rsrs_length);
+  rpos = 0;
+  rend = 0;
+  wpos = 0;
+  while (rpos < rsrs_length)
+  {
+    unsigned int mlen = sizeof (struct TALER_CRYPTO_BatchSignRequest);
+
+    while ( (rend < rsrs_length) &&
+            (mlen
+             + sizeof (struct TALER_CRYPTO_SignRequest)
+             + rsrs[rend].msg_size < UINT16_MAX) )
+    {
+      mlen += sizeof (struct TALER_CRYPTO_SignRequest) + rsrs[rend].msg_size;
+      rend++;
+    }
+    {
+      char obuf[mlen] GNUNET_ALIGN;
+      struct TALER_CRYPTO_BatchSignRequest *bsr
+        = (struct TALER_CRYPTO_BatchSignRequest *) obuf;
+      void *wbuf;
+
+      bsr->header.type = htons (TALER_HELPER_RSA_MT_REQ_BATCH_SIGN);
+      bsr->header.size = htons (mlen);
+      bsr->batch_size = htonl (rend - rpos);
+      wbuf = &bsr[1];
+      for (unsigned int i = rpos; i<rend; i++)
+      {
+        struct TALER_CRYPTO_SignRequest *sr = wbuf;
+        const struct TALER_CRYPTO_RsaSignRequest *rsr = &rsrs[i];
+
+        sr->header.type = htons (TALER_HELPER_RSA_MT_REQ_SIGN);
+        sr->header.size = htons (sizeof (*sr) + rsr->msg_size);
+        sr->reserved = htonl (0);
+        sr->h_rsa = *rsr->h_rsa;
+        GNUNET_memcpy (&sr[1],
+                       rsr->msg,
+                       rsr->msg_size);
+        wbuf += sizeof (*sr) + rsr->msg_size;
+      }
+      GNUNET_assert (wbuf == &obuf[mlen]);
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Sending batch request [%u-%u)\n",
+                  rpos,
+                  rend);
+      if (GNUNET_OK !=
+          TALER_crypto_helper_send_all (dh->sock,
+                                        obuf,
+                                        sizeof (obuf)))
+      {
+        GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                             "send");
+        do_disconnect (dh);
+        return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+      }
+    }
+    rpos = rend;
+    {
+      char buf[UINT16_MAX];
+      size_t off = 0;
+      const struct GNUNET_MessageHeader *hdr
+        = (const struct GNUNET_MessageHeader *) buf;
+      bool finished = false;
+
+      while (1)
+      {
+        uint16_t msize;
+        ssize_t ret;
+
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Awaiting reply at %u (up to %u)\n",
+                    wpos,
+                    rend);
+        ret = recv (dh->sock,
+                    &buf[off],
+                    sizeof (buf) - off,
+                    (finished && (0 == off))
+                  ? MSG_DONTWAIT
+                  : 0);
+        if (ret < 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 (dh);
+          ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+          break;
+        }
+        if (0 == ret)
+        {
+          GNUNET_break (0 == off);
+          if (! finished)
+            ec = TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
+          if (TALER_EC_NONE == ec)
+            break;
+          return ec;
+        }
+        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_RES_SIGNATURE:
+          if (msize < sizeof (struct TALER_CRYPTO_SignResponse))
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          }
+          if (finished)
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          }
+          {
+            const struct TALER_CRYPTO_SignResponse *sr =
+              (const struct TALER_CRYPTO_SignResponse *) buf;
+            struct GNUNET_CRYPTO_RsaSignature *rsa_signature;
+            struct GNUNET_CRYPTO_BlindedSignature *blind_sig;
+
+            rsa_signature = GNUNET_CRYPTO_rsa_signature_decode (
+              &sr[1],
+              msize - sizeof (*sr));
+            if (NULL == rsa_signature)
+            {
+              GNUNET_break_op (0);
+              do_disconnect (dh);
+              return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+            }
+            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                        "Received %u signature\n",
+                        wpos);
+            blind_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature);
+            blind_sig->cipher = GNUNET_CRYPTO_BSA_RSA;
+            blind_sig->rc = 1;
+            blind_sig->details.blinded_rsa_signature = rsa_signature;
+            bss[wpos].blinded_sig = blind_sig;
+            wpos++;
+            if (wpos == rend)
+            {
+              if (TALER_EC_INVALID == ec)
+                ec = TALER_EC_NONE;
+              finished = true;
+            }
+            break;
+          }
+        case TALER_HELPER_RSA_MT_RES_SIGN_FAILURE:
+          if (msize != sizeof (struct TALER_CRYPTO_SignFailure))
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          }
+          {
+            const struct TALER_CRYPTO_SignFailure *sf =
+              (const struct TALER_CRYPTO_SignFailure *) buf;
+
+            ec = (enum TALER_ErrorCode) ntohl (sf->ec);
+            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                        "Signing %u failed with status %d!\n",
+                        wpos,
+                        ec);
+            wpos++;
+            if (wpos == rend)
+            {
+              finished = true;
+            }
+            break;
+          }
+        case TALER_HELPER_RSA_MT_AVAIL:
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Received new key!\n");
+          if (GNUNET_OK !=
+              handle_mt_avail (dh,
+                               hdr))
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          }
+          break; /* while(1) loop ensures we recvfrom() again */
+        case TALER_HELPER_RSA_MT_PURGE:
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Received revocation!\n");
+          if (GNUNET_OK !=
+              handle_mt_purge (dh,
+                               hdr))
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          }
+          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);
+          return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+        }
+        memmove (buf,
+                 &buf[msize],
+                 off - msize);
+        off -= msize;
+        goto more;
+      } /* while(1) */
+    } /* scope */
+  }   /* while (rpos < rsrs_length) */
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Existing with %u signatures and status %d\n",
+              wpos,
+              ec);
+  return ec;
+}
+
+
+void
+DONAU_CRYPTO_helper_rsa_revoke (
+  struct DONAU_CRYPTO_RsaDonationUnitHelper *dh,
+  const struct TALER_RsaPubHashP *h_rsa)
+{
+  struct TALER_CRYPTO_RevokeRequest rr = {
+    .header.size = htons (sizeof (rr)),
+    .header.type = htons (TALER_HELPER_RSA_MT_REQ_REVOKE),
+    .h_rsa = *h_rsa
+  };
+
+  if (GNUNET_OK !=
+      try_connect (dh))
+    return; /* give up */
+  if (GNUNET_OK !=
+      TALER_crypto_helper_send_all (dh->sock,
+                                    &rr,
+                                    sizeof (rr)))
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                         "send");
+    do_disconnect (dh);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Requested revocation of donation unit key %s\n",
+              GNUNET_h2s (&h_rsa->hash));
+}
+
+
+void
+DONAU_CRYPTO_helper_rsa_disconnect (
+  struct DONAU_CRYPTO_RsaDonationUnitHelper *dh)
+{
+  if (-1 != dh->sock)
+    do_disconnect (dh);
+  GNUNET_free (dh);
+}
+
+
+/* end of crypto_helper_donation_unit.c */

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



reply via email to

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