gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated: first sketch of eddsa helper


From: gnunet
Subject: [taler-exchange] branch master updated: first sketch of eddsa helper
Date: Sun, 22 Nov 2020 23:31:37 +0100

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

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new c0a6e13b first sketch of eddsa helper
c0a6e13b is described below

commit c0a6e13b3c8bd7a8321bb11fde917869d67dcbc1
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sun Nov 22 23:31:35 2020 +0100

    first sketch of eddsa helper
---
 src/util/.gitignore                  |    1 +
 src/util/Makefile.am                 |   10 +
 src/util/taler-helper-crypto-eddsa.c | 1563 ++++++++++++++++++++++++++++++++++
 src/util/taler-helper-crypto-eddsa.h |  188 ++++
 4 files changed, 1762 insertions(+)

diff --git a/src/util/.gitignore b/src/util/.gitignore
index 8a8cc052..656403c5 100644
--- a/src/util/.gitignore
+++ b/src/util/.gitignore
@@ -1,4 +1,5 @@
 taler-config
 test_payto
 taler-helper-crypto-rsa
+taler-helper-crypto-eddsa
 test_helper_rsa
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index 73edce2c..5663c623 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -21,6 +21,7 @@ EXTRA_DIST = \
   test_helper_rsa.conf
 
 libexec_PROGRAMS = \
+  taler-helper-crypto-eddsa \
   taler-helper-crypto-rsa
 
 bin_SCRIPTS = \
@@ -44,6 +45,15 @@ taler_helper_crypto_rsa_LDADD = \
   $(LIBGCRYPT_LIBS) \
   $(XLIB)
 
+taler_helper_crypto_eddsa_SOURCES = \
+  taler-helper-crypto-eddsa.c taler-helper-crypto-eddsa.h
+taler_helper_crypto_eddsa_LDADD = \
+  libtalerutil.la \
+  -lgnunetutil \
+  -lpthread \
+  $(LIBGCRYPT_LIBS) \
+  $(XLIB)
+
 lib_LTLIBRARIES = \
   libtalerutil.la
 
diff --git a/src/util/taler-helper-crypto-eddsa.c 
b/src/util/taler-helper-crypto-eddsa.c
new file mode 100644
index 00000000..2f6a6cc9
--- /dev/null
+++ b/src/util/taler-helper-crypto-eddsa.c
@@ -0,0 +1,1563 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2020 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file util/taler-helper-crypto-eddsa.c
+ * @brief Standalone process to perform private key EDDSA operations
+ * @author Christian Grothoff
+ *
+ * INTEGRATION NOTES:
+ * - Option 'DURATION_OVERLAP' renamed to 'OVERLAP_DURATION' for consistency;
+ *   => need to update in deployment scripts and default configuration!
+ * - option 'KEY_DIR' moved from section 'exchange' to 
'taler-helper-crypto-eddsa'!
+ *
+ * Key design points:
+ * - EVERY thread of the exchange will have its own pair of connections to the
+ *   crypto helpers.  This way, every threat will also have its own /keys state
+ *   and avoid the need to synchronize on those.
+ * - auditor signatures and master signatures are to be kept in the exchange 
DB,
+ *   and merged with the public keys of the helper by the exchange HTTPD!
+ * - the main loop of the helper is SINGLE-THREADED, but there are
+ *   threads for crypto-workers which (only) do the signing in parallel,
+ *   working of a work-queue.
+ * - thread-safety: signing happens in parallel, thus when REMOVING private 
keys,
+ *   we must ensure that all signers are done before we fully free() the
+ *   private key. This is done by reference counting (as work is always
+ *   assigned and collected by the main thread).
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include "taler-helper-crypto-eddsa.h"
+#include <gcrypt.h>
+#include <pthread.h>
+#include <sys/eventfd.h>
+#include "taler_error_codes.h"
+
+/**
+ * One particular key.
+ */
+struct Key
+{
+
+  /**
+   * Kept in a DLL. Sorted by anchor time.
+   */
+  struct Key *next;
+
+  /**
+   * Kept in a DLL. Sorted by anchor time.
+   */
+  struct Key *prev;
+
+  /**
+   * Name of the file this key is stored under.
+   */
+  char *filename;
+
+  /**
+   * The private key.
+   */
+  struct TALER_ExchangePrivateKeyP exchange_priv;
+
+  /**
+   * The public key.
+   */
+  struct TALER_ExchangePublicKeyP exchange_pub;
+
+  /**
+   * Time at which this key is supposed to become valid.
+   */
+  struct GNUNET_TIME_Absolute anchor;
+
+  /**
+   * Reference counter. Counts the number of threads that are
+   * using this key at this time.
+   */
+  unsigned int rc;
+
+  /**
+   * Flag set to true if this key has been purged and the memory
+   * must be freed as soon as @e rc hits zero.
+   */
+  bool purge;
+
+};
+
+
+/**
+ * Information we keep for a client connected to us.
+ */
+struct Client
+{
+
+  /**
+   * Kept in a DLL.
+   */
+  struct Client *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct Client *prev;
+
+  /**
+   * Client address.
+   */
+  struct sockaddr_un addr;
+
+  /**
+   * Number of bytes used in @e addr.
+   */
+  socklen_t addr_size;
+
+};
+
+
+struct WorkItem
+{
+
+  /**
+   * Kept in a DLL.
+   */
+  struct WorkItem *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct WorkItem *prev;
+
+  /**
+   * Key to be used for this operation.
+   */
+  struct Key *key;
+
+  /**
+   * EDDSA signature over @e msg using @e key. Result of doing the work.
+   */
+  struct TALER_ExchangeSignatureP signature;
+
+  /**
+   * Message to sign.
+   */
+  struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
+
+  /**
+   * Client address.
+   */
+  struct sockaddr_un addr;
+
+  /**
+   * Number of bytes used in @e addr.
+   */
+  socklen_t addr_size;
+
+  /**
+   * Operation status code.
+   */
+  enum TALER_ErrorCode ec;
+
+};
+
+
+/**
+ * Head of DLL of actual keys, sorted by anchor.
+ */
+static struct Key *keys_head;
+
+/**
+ * Tail of DLL of actual keys.
+ */
+static struct Key *keys_tail;
+
+/**
+ * How long can a key be used?
+ */
+static struct GNUNET_TIME_Relative duration;
+
+/**
+ * Return value from main().
+ */
+static int global_ret;
+
+/**
+ * Number of worker threads to use. Default (0) is to use one per CPU core
+ * available.
+ * Length of the #workers array.
+ */
+static unsigned int num_workers;
+
+/**
+ * Time when the key update is executed.
+ * Either the actual current time, or a pretended time.
+ */
+static struct GNUNET_TIME_Absolute now;
+
+/**
+ * The time for the key update, as passed by the user
+ * on the command line.
+ */
+static struct GNUNET_TIME_Absolute now_tmp;
+
+/**
+ * Handle to the exchange's configuration
+ */
+static const struct GNUNET_CONFIGURATION_Handle *kcfg;
+
+/**
+ * Where do we store the keys?
+ */
+static char *keydir;
+
+/**
+ * How much should coin creation duration overlap
+ * with the next key?  Basically, the starting time of two
+ * keys is always #duration - #duration_overlap apart.
+ */
+static struct GNUNET_TIME_Relative overlap_duration;
+
+/**
+ * How long into the future do we pre-generate keys?
+ */
+static struct GNUNET_TIME_Relative lookahead_sign;
+
+/**
+ * Our listen socket.
+ */
+static struct GNUNET_NETWORK_Handle *unix_sock;
+
+/**
+ * Path where we are listening.
+ */
+static char *unixpath;
+
+/**
+ * Task run to accept new inbound connections.
+ */
+static struct GNUNET_SCHEDULER_Task *read_task;
+
+/**
+ * Task run to generate new keys.
+ */
+static struct GNUNET_SCHEDULER_Task *keygen_task;
+
+/**
+ * Head of DLL of clients connected to us.
+ */
+static struct Client *clients_head;
+
+/**
+ * Tail of DLL of clients connected to us.
+ */
+static struct Client *clients_tail;
+
+/**
+ * Head of DLL with pending signing operations.
+ */
+static struct WorkItem *work_head;
+
+/**
+ * Tail of DLL with pending signing operations.
+ */
+static struct WorkItem *work_tail;
+
+/**
+ * Lock for the work queue.
+ */
+static pthread_mutex_t work_lock;
+
+/**
+ * Condition variable for the semaphore of the work queue.
+ */
+static pthread_cond_t work_cond = PTHREAD_COND_INITIALIZER;
+
+/**
+ * Number of items in the work queue. Also used as the semaphore counter.
+ */
+static unsigned long long work_counter;
+
+/**
+ * Head of DLL with completed signing operations.
+ */
+static struct WorkItem *done_head;
+
+/**
+ * Tail of DLL with completed signing operations.
+ */
+static struct WorkItem *done_tail;
+
+/**
+ * Lock for the done queue.
+ */
+static pthread_mutex_t done_lock;
+
+/**
+ * Task waiting for work to be done.
+ */
+static struct GNUNET_SCHEDULER_Task *done_task;
+
+/**
+ * Signal used by threads to notify the #done_task that they
+ * completed work that is now in the done queue.
+ */
+static struct GNUNET_NETWORK_Handle *done_signal;
+
+/**
+ * Set once we are in shutdown and workers should terminate.
+ */
+static volatile bool in_shutdown;
+
+/**
+ * Array of #num_worker sign_worker() threads.
+ */
+static pthread_t *workers;
+
+
+/**
+ * Main function of a worker thread that signs.
+ *
+ * @param cls NULL
+ * @return NULL
+ */
+static void *
+sign_worker (void *cls)
+{
+  (void) cls;
+  GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
+  while (! in_shutdown)
+  {
+    struct WorkItem *wi;
+
+    while (NULL != (wi = work_head))
+    {
+      /* take work from queue */
+      GNUNET_CONTAINER_DLL_remove (work_head,
+                                   work_tail,
+                                   wi);
+      work_counter--;
+      GNUNET_assert (0 == pthread_mutex_unlock (&work_lock));
+      {
+        if (GNUNET_OK !=
+            GNUNET_CRYPTO_eddsa_sign_ (&wi->key->exchange_priv.eddsa_priv,
+                                       wi->purpose,
+                                       &wi->signature.eddsa_signature))
+          wi->ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+        else
+          wi->ec = TALER_EC_NONE;
+      }
+      /* put completed work into done queue */
+      GNUNET_assert (0 == pthread_mutex_lock (&done_lock));
+      GNUNET_CONTAINER_DLL_insert (done_head,
+                                   done_tail,
+                                   wi);
+      GNUNET_assert (0 == pthread_mutex_unlock (&done_lock));
+      {
+        uint64_t val = GNUNET_htonll (1);
+
+        /* raise #done_signal */
+        if (sizeof(val) !=
+            write (GNUNET_NETWORK_get_fd (done_signal),
+                   &val,
+                   sizeof (val)))
+          GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                               "write(eventfd)");
+      }
+      GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
+    }
+    /* queue is empty, wait for work */
+    GNUNET_assert (0 ==
+                   pthread_cond_wait (&work_cond,
+                                      &work_lock));
+  }
+  GNUNET_assert (0 ==
+                 pthread_mutex_unlock (&work_lock));
+  return NULL;
+}
+
+
+/**
+ * Free @a client, releasing all (remaining) state.
+ *
+ * @param[in] client data to free
+ */
+static void
+free_client (struct Client *client)
+{
+  GNUNET_CONTAINER_DLL_remove (clients_head,
+                               clients_tail,
+                               client);
+  GNUNET_free (client);
+}
+
+
+/**
+ * Function run to read incoming requests from a client.
+ *
+ * @param cls the `struct Client`
+ */
+static void
+read_job (void *cls);
+
+
+/**
+ * Free @a key. It must already have been removed from the DLL.
+ *
+ * @param[in] key the key to free
+ */
+static void
+free_key (struct Key *key)
+{
+  GNUNET_free (key->filename);
+  GNUNET_free (key);
+}
+
+
+/**
+ * Send a message starting with @a hdr to @a client.
+ *
+ * @param addr address where to send the message
+ * @param addr_size number of bytes in @a addr
+ * @param hdr beginning of the message, length indicated in size field
+ * @return #GNUNET_OK on success
+ */
+static int
+transmit (const struct sockaddr_un *addr,
+          socklen_t addr_size,
+          const struct GNUNET_MessageHeader *hdr)
+{
+  ssize_t ret;
+
+  ret = GNUNET_NETWORK_socket_sendto (unix_sock,
+                                      hdr,
+                                      ntohs (hdr->size),
+                                      (const struct sockaddr *) addr,
+                                      addr_size);
+  if (ret != ntohs (hdr->size))
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO,
+                         "sendto");
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Process completed tasks that are in the #done_head queue, sending
+ * the result back to the client (and resuming the client).
+ *
+ * @param cls NULL
+ */
+static void
+handle_done (void *cls)
+{
+  uint64_t data;
+  (void) cls;
+
+  /* consume #done_signal */
+  if (sizeof (data) !=
+      read (GNUNET_NETWORK_get_fd (done_signal),
+            &data,
+            sizeof (data)))
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                         "read(eventfd)");
+  done_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                             done_signal,
+                                             &handle_done,
+                                             NULL);
+  GNUNET_assert (0 == pthread_mutex_lock (&done_lock));
+  while (NULL != done_head)
+  {
+    struct WorkItem *wi = done_head;
+
+    GNUNET_CONTAINER_DLL_remove (done_head,
+                                 done_tail,
+                                 wi);
+    GNUNET_assert (0 == pthread_mutex_unlock (&done_lock));
+    if (TALER_EC_NONE != wi->ec)
+    {
+      struct TALER_CRYPTO_SignFailure sf = {
+        .header.size = htons (sizeof (sf)),
+        .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE),
+        .ec = htonl (wi->ec)
+      };
+
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Signing request failed, worker failed to produce 
signature\n");
+      (void) transmit (&wi->addr,
+                       wi->addr_size,
+                       &sf.header);
+    }
+    else
+    {
+      struct TALER_CRYPTO_SignResponse sr = {
+        .header.size = htons (sizeof (sr)),
+        .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGNATURE),
+        .exchange_pub = wi->key->exchange_pub,
+        .exchange_sig = wi->signature
+      };
+
+      (void) transmit (&wi->addr,
+                       wi->addr_size,
+                       &sr.header);
+    }
+    {
+      struct Key *key = wi->key;
+
+      key->rc--;
+      if ( (0 == key->rc) &&
+           (key->purge) )
+        free_key (key);
+    }
+    GNUNET_free (wi);
+    GNUNET_assert (0 == pthread_mutex_lock (&done_lock));
+  }
+  GNUNET_assert (0 == pthread_mutex_unlock (&done_lock));
+
+}
+
+
+/**
+ * Handle @a client request @a sr to create signature. Create the
+ * signature using the respective key and return the result to
+ * the client.
+ *
+ * @param addr address of the client making the request
+ * @param addr_size number of bytes in @a addr
+ * @param sr the request details
+ */
+static void
+handle_sign_request (const struct sockaddr_un *addr,
+                     socklen_t addr_size,
+                     const struct TALER_CRYPTO_SignRequest *sr)
+{
+  const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose = &sr->purpose;
+  struct WorkItem *wi;
+  size_t purpose_size = ntohs (sr->header.size) - sizeof (*sr)
+                        + sizeof (*purpose);
+
+  if (purpose_size != htonl (purpose->size))
+  {
+    struct TALER_CRYPTO_SignFailure sf = {
+      .header.size = htons (sizeof (sr)),
+      .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE),
+      .ec = htonl (TALER_EC_GENERIC_PARAMETER_MALFORMED)
+    };
+
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Signing request failed, request malformed\n");
+    (void) transmit (addr,
+                     addr_size,
+                     &sf.header);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Received request to sign over %u bytes\n",
+              (unsigned int) purpose_size);
+  wi = GNUNET_new (struct WorkItem);
+  wi->addr = *addr;
+  wi->addr_size = addr_size;
+  wi->key = keys_head;
+  keys_head->rc++;
+  wi->purpose = GNUNET_memdup (purpose,
+                               purpose_size);
+  GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
+  work_counter++;
+  GNUNET_CONTAINER_DLL_insert (work_head,
+                               work_tail,
+                               wi);
+  GNUNET_assert (0 == pthread_mutex_unlock (&work_lock));
+  GNUNET_assert (0 == pthread_cond_signal (&work_cond));
+}
+
+
+/**
+ * Notify @a client about @a key becoming available.
+ *
+ * @param[in,out] client the client to notify; possible freed if transmission 
fails
+ * @param key the key to notify @a client about
+ * @return #GNUNET_OK on success
+ */
+static int
+notify_client_key_add (struct Client *client,
+                       const struct Key *key)
+{
+  struct TALER_CRYPTO_EddsaKeyAvailableNotification an = {
+    .header.size = htons (sizeof (an)),
+    .header.type = htons (TALER_HELPER_EDDSA_MT_AVAIL),
+    .anchor_time = GNUNET_TIME_absolute_hton (key->anchor),
+    .duration = GNUNET_TIME_relative_hton (duration),
+    .exchange_pub = key->exchange_pub
+  };
+
+  if (GNUNET_OK !=
+      transmit (&client->addr,
+                client->addr_size,
+                &an.header))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Client %s must have disconnected\n",
+                client->addr.sun_path);
+    free_client (client);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Notify @a client about @a key being purged.
+ *
+ * @param[in,out] client the client to notify; possible freed if transmission 
fails
+ * @param key the key to notify @a client about
+ * @return #GNUNET_OK on success
+ */
+static int
+notify_client_key_del (struct Client *client,
+                       const struct Key *key)
+{
+  struct TALER_CRYPTO_EddsaKeyPurgeNotification pn = {
+    .header.type = htons (TALER_HELPER_EDDSA_MT_PURGE),
+    .header.size = htons (sizeof (pn)),
+    .exchange_pub = key->exchange_pub
+  };
+
+  if (GNUNET_OK !=
+      transmit (&client->addr,
+                client->addr_size,
+                &pn.header))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Client %s must have disconnected\n",
+                client->addr.sun_path);
+    free_client (client);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Initialize key material for key @a key (also on disk).
+ *
+ * @param[in,out] key to compute key material for
+ * @param position where in the DLL will the @a key go
+ * @return #GNUNET_OK on success
+ */
+static int
+setup_key (struct Key *key,
+           struct Key *position)
+{
+  struct GNUNET_CRYPTO_EddsaPrivateKey priv;
+  struct GNUNET_CRYPTO_EddsaPublicKey pub;
+
+  GNUNET_CRYPTO_eddsa_key_create (&priv);
+  GNUNET_CRYPTO_eddsa_key_get_public (&priv,
+                                      &pub);
+  GNUNET_asprintf (&key->filename,
+                   "%s/%llu",
+                   keydir,
+                   (unsigned long long) (key->anchor.abs_value_us
+                                         / 
GNUNET_TIME_UNIT_SECONDS.rel_value_us));
+  if (sizeof (priv) !=
+      GNUNET_DISK_fn_write (key->filename,
+                            &priv,
+                            sizeof (priv),
+                            GNUNET_DISK_PERM_USER_READ))
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "write",
+                              key->filename);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Setup fresh private key in `%s'\n",
+              key->filename);
+  key->exchange_priv.eddsa_priv = priv;
+  key->exchange_pub.eddsa_pub = pub;
+  GNUNET_CONTAINER_DLL_insert_after (keys_head,
+                                     keys_tail,
+                                     position,
+                                     key);
+
+  /* tell clients about new key */
+  {
+    struct Client *nxt;
+
+    for (struct Client *client = clients_head;
+         NULL != client;
+         client = nxt)
+    {
+      nxt = client->next;
+      if (GNUNET_OK !=
+          notify_client_key_add (client,
+                                 key))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                    "Failed to notify client about new key, client dropped\n");
+      }
+    }
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * A client informs us that a key has been revoked.
+ * Check if the key is still in use, and if so replace (!)
+ * it with a fresh key.
+ *
+ * @param addr address of the client making the request
+ * @param addr_size number of bytes in @a addr
+ * @param rr the revocation request
+ */
+static void
+handle_revoke_request (const struct sockaddr_un *addr,
+                       socklen_t addr_size,
+                       const struct TALER_CRYPTO_RevokeRequest *rr)
+{
+  struct Key *key;
+  struct Key *nkey;
+
+  nkey = NULL;
+  for (struct Key *pos = keys_head; NULL != pos; pos = pos->next)
+    if (0 == GNUNET_memcmp (&pos->exchange_pub,
+                            &rr->exchange_pub))
+    {
+      key = pos;
+      break;
+    }
+  if (NULL == key)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Revocation request ignored, key unknown\n");
+    return;
+  }
+
+  /* kill existing key, done first to ensure this always happens */
+  if (0 != unlink (key->filename))
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "unlink",
+                              key->filename);
+
+  /* Setup replacement key */
+  nkey = GNUNET_new (struct Key);
+  nkey->anchor = key->anchor;
+  if (GNUNET_OK !=
+      setup_key (nkey,
+                 key))
+  {
+    GNUNET_break (0);
+    GNUNET_SCHEDULER_shutdown ();
+    global_ret = 44;
+    return;
+  }
+
+  /* get rid of the old key */
+  key->purge = true;
+  GNUNET_CONTAINER_DLL_remove (keys_head,
+                               keys_tail,
+                               key);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Revocation complete\n");
+
+  /* Tell clients this key is gone */
+  {
+    struct Client *nxt;
+
+    for (struct Client *client = clients_head;
+         NULL != client;
+         client = nxt)
+    {
+      nxt = client->next;
+      if (GNUNET_OK !=
+          notify_client_key_del (client,
+                                 key))
+        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                    "Failed to notify client about revoked key, client 
dropped\n");
+    }
+  }
+  if (0 == key->rc)
+    free_key (key);
+}
+
+
+static void
+read_job (void *cls)
+{
+  struct Client *client = cls;
+  char buf[65536];
+  ssize_t buf_size;
+  const struct GNUNET_MessageHeader *hdr;
+  struct sockaddr_un addr;
+  socklen_t addr_size = sizeof (addr);
+
+  read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                             unix_sock,
+                                             &read_job,
+                                             NULL);
+  buf_size = GNUNET_NETWORK_socket_recvfrom (unix_sock,
+                                             buf,
+                                             sizeof (buf),
+                                             (struct sockaddr *) &addr,
+                                             &addr_size);
+  if (-1 == buf_size)
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                         "recv");
+    return;
+  }
+  if (0 == buf_size)
+  {
+    return;
+  }
+  if (buf_size < sizeof (struct GNUNET_MessageHeader))
+  {
+    GNUNET_break_op (0);
+    return;
+  }
+  hdr = (const struct GNUNET_MessageHeader *) buf;
+  if (ntohs (hdr->size) != buf_size)
+  {
+    GNUNET_break_op (0);
+    free_client (client);
+    return;
+  }
+  switch (ntohs (hdr->type))
+  {
+  case TALER_HELPER_EDDSA_MT_REQ_INIT:
+    if (ntohs (hdr->size) != sizeof (struct GNUNET_MessageHeader))
+    {
+      GNUNET_break_op (0);
+      return;
+    }
+    {
+      struct Client *client;
+
+      client = GNUNET_new (struct Client);
+      client->addr = addr;
+      client->addr_size = addr_size;
+      GNUNET_CONTAINER_DLL_insert (clients_head,
+                                   clients_tail,
+                                   client);
+      for (struct Key *key = keys_head;
+           NULL != key;
+           key = key->next)
+      {
+        if (GNUNET_OK !=
+            notify_client_key_add (client,
+                                   key))
+        {
+          /* client died, skip the rest */
+          break;
+        }
+      }
+    }
+    break;
+  case TALER_HELPER_EDDSA_MT_REQ_SIGN:
+    if (ntohs (hdr->size) <= sizeof (struct TALER_CRYPTO_SignRequest))
+    {
+      GNUNET_break_op (0);
+      return;
+    }
+    handle_sign_request (&addr,
+                         addr_size,
+                         (const struct TALER_CRYPTO_SignRequest *) buf);
+    break;
+  case TALER_HELPER_EDDSA_MT_REQ_REVOKE:
+    if (ntohs (hdr->size) != sizeof (struct TALER_CRYPTO_RevokeRequest))
+    {
+      GNUNET_break_op (0);
+      return;
+    }
+    handle_revoke_request (&addr,
+                           addr_size,
+                           (const struct TALER_CRYPTO_RevokeRequest *) buf);
+    break;
+  default:
+    GNUNET_break_op (0);
+    return;
+  }
+}
+
+
+/**
+ * Create a new key (we do not have enough).
+ *
+ * @return #GNUNET_OK on success
+ */
+static int
+create_key (void)
+{
+  struct Key *key;
+  struct GNUNET_TIME_Absolute anchor;
+  struct GNUNET_TIME_Absolute now;
+
+  now = GNUNET_TIME_absolute_get ();
+  (void) GNUNET_TIME_round_abs (&now);
+  if (NULL == keys_tail)
+  {
+    anchor = now;
+  }
+  else
+  {
+    anchor = GNUNET_TIME_absolute_add (keys_tail->anchor,
+                                       GNUNET_TIME_relative_subtract (
+                                         duration,
+                                         overlap_duration));
+    if (now.abs_value_us > anchor.abs_value_us)
+      anchor = now;
+  }
+  key = GNUNET_new (struct Key);
+  key->anchor = anchor;
+  if (GNUNET_OK !=
+      setup_key (key,
+                 keys_tail))
+  {
+    GNUNET_free (key);
+    GNUNET_SCHEDULER_shutdown ();
+    global_ret = 42;
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * At what time does the current key set require its next action?  Basically,
+ * the minimum of the expiration time of the oldest key, and the expiration
+ * time of the newest key minus the #lookahead_sign time.
+ */
+static struct GNUNET_TIME_Absolute
+key_action_time (void)
+{
+  return GNUNET_TIME_absolute_min (
+    GNUNET_TIME_absolute_add (keys_head->anchor,
+                              duration),
+    GNUNET_TIME_absolute_subtract (
+      GNUNET_TIME_absolute_subtract (
+        GNUNET_TIME_absolute_add (keys_tail->anchor,
+                                  duration),
+        lookahead_sign),
+      overlap_duration));
+}
+
+
+/**
+ * The validity period of a key @a key has expired. Purge it.
+ *
+ * @param[in] key expired key to purge and free
+ */
+static void
+purge_key (struct Key *key)
+{
+  struct Client *nxt;
+
+  for (struct Client *client = clients_head;
+       NULL != client;
+       client = nxt)
+  {
+    nxt = client->next;
+    if (GNUNET_OK !=
+        notify_client_key_del (client,
+                               key))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Failed to notify client about purged key, client 
dropped\n");
+    }
+  }
+  GNUNET_CONTAINER_DLL_remove (keys_head,
+                               keys_tail,
+                               key);
+  if (0 != unlink (key->filename))
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "unlink",
+                              key->filename);
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Purged expired private key `%s'\n",
+                key->filename);
+  }
+  GNUNET_free (key->filename);
+  if (0 != key->rc)
+  {
+    /* delay until all signing threads are done with this key */
+    key->purge = true;
+    return;
+  }
+  GNUNET_free (key);
+}
+
+
+/**
+ * Create new keys and expire ancient keys.
+ *
+ * @param cls NULL
+ */
+static void
+update_keys (void *cls)
+{
+  (void) cls;
+
+  keygen_task = NULL;
+  /* create new keys */
+  while ( (NULL == keys_tail) ||
+          (0 ==
+           GNUNET_TIME_absolute_get_remaining (
+             GNUNET_TIME_absolute_subtract (
+               GNUNET_TIME_absolute_subtract (
+                 GNUNET_TIME_absolute_add (keys_tail->anchor,
+                                           duration),
+                 lookahead_sign),
+               overlap_duration)).rel_value_us) )
+    GNUNET_assert (GNUNET_OK ==
+                   create_key ());
+  /* remove expired keys */
+  while ( (NULL != keys_head) &&
+          (0 ==
+           GNUNET_TIME_absolute_get_remaining
+             (GNUNET_TIME_absolute_add (keys_head->anchor,
+                                        duration)).rel_value_us) )
+    purge_key (keys_head);
+  keygen_task = GNUNET_SCHEDULER_add_at (key_action_time (),
+                                         &update_keys,
+                                         NULL);
+}
+
+
+/**
+ * Parse private key from @a filename in @a buf.
+ *
+ * @param filename name of the file we are parsing, for logging
+ * @param buf key material
+ * @param buf_size number of bytes in @a buf
+ */
+static void
+parse_key (const char *filename,
+           const void *buf,
+           size_t buf_size)
+{
+  struct GNUNET_CRYPTO_EddsaPrivateKey priv;
+  char *anchor_s;
+  char dummy;
+  unsigned long long anchor_ll;
+  struct GNUNET_TIME_Absolute anchor;
+
+  anchor_s = strrchr (filename,
+                      '/');
+  if (NULL == anchor_s)
+  {
+    /* File in a directory without '/' in the name, this makes no sense. */
+    GNUNET_break (0);
+    return;
+  }
+  anchor_s++;
+  if (1 != sscanf (anchor_s,
+                   "%llu%c",
+                   &anchor_ll,
+                   &dummy))
+  {
+    /* Filenames in KEYDIR must ONLY be the anchor time in seconds! */
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Filename `%s' invalid for key file, skipping\n",
+                filename);
+    return;
+  }
+  anchor.abs_value_us = anchor_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
+  if (anchor_ll != anchor.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us)
+  {
+    /* Integer overflow. Bad, invalid filename. */
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Filename `%s' invalid for key file, skipping\n",
+                filename);
+    return;
+  }
+  if (buf_size != sizeof (priv))
+  {
+    /* Parser failure. */
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "File `%s' is malformed, skipping\n",
+                filename);
+    return;
+  }
+  memcpy (&priv,
+          buf,
+          buf_size);
+
+  {
+    struct GNUNET_CRYPTO_EddsaPublicKey pub;
+    struct Key *key;
+    struct Key *before;
+
+    GNUNET_CRYPTO_eddsa_key_get_public (&priv,
+                                        &pub);
+    key = GNUNET_new (struct Key);
+    key->exchange_priv.eddsa_priv = priv;
+    key->exchange_pub.eddsa_pub = pub;
+    key->anchor = anchor;
+    key->filename = GNUNET_strdup (filename);
+    before = NULL;
+    for (struct Key *pos = keys_head;
+         NULL != pos;
+         pos = pos->next)
+    {
+      if (pos->anchor.abs_value_us > anchor.abs_value_us)
+        break;
+      before = pos;
+    }
+    GNUNET_CONTAINER_DLL_insert_after (keys_head,
+                                       keys_tail,
+                                       before,
+                                       key);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Imported key from `%s'\n",
+                filename);
+  }
+}
+
+
+/**
+ * Import a private key from @a filename.
+ *
+ * @param cls NULL
+ * @param filename name of a file in the directory
+ */
+static int
+import_key (void *cls,
+            const char *filename)
+{
+  struct GNUNET_DISK_FileHandle *fh;
+  struct GNUNET_DISK_MapHandle *map;
+  void *ptr;
+  int fd;
+  struct stat sbuf;
+
+  {
+    struct stat lsbuf;
+
+    if (0 != lstat (filename,
+                    &lsbuf))
+    {
+      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+                                "lstat",
+                                filename);
+      return GNUNET_OK;
+    }
+    if (! S_ISREG (lsbuf.st_mode))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "File `%s' is not a regular file, which is not allowed for 
private keys!\n",
+                  filename);
+      return GNUNET_OK;
+    }
+  }
+
+  fd = open (filename,
+             O_CLOEXEC);
+  if (-1 == fd)
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+                              "open",
+                              filename);
+    return GNUNET_OK;
+  }
+  if (0 != fstat (fd,
+                  &sbuf))
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+                              "stat",
+                              filename);
+    return GNUNET_OK;
+  }
+  if (! S_ISREG (sbuf.st_mode))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "File `%s' is not a regular file, which is not allowed for 
private keys!\n",
+                filename);
+    return GNUNET_OK;
+  }
+  if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO)))
+  {
+    /* permission are NOT tight, try to patch them up! */
+    if (0 !=
+        fchmod (fd,
+                S_IRUSR))
+    {
+      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+                                "fchmod",
+                                filename);
+      /* refuse to use key if file has wrong permissions */
+      GNUNET_break (0 == close (fd));
+      return GNUNET_OK;
+    }
+  }
+  fh = GNUNET_DISK_get_handle_from_int_fd (fd);
+  if (NULL == fh)
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+                              "open",
+                              filename);
+    GNUNET_break (0 == close (fd));
+    return GNUNET_OK;
+  }
+  if (sbuf.st_size > 2048)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "File `%s' to big to be a private key\n",
+                filename);
+    GNUNET_DISK_file_close (fh);
+    return GNUNET_OK;
+  }
+  ptr = GNUNET_DISK_file_map (fh,
+                              &map,
+                              GNUNET_DISK_MAP_TYPE_READ,
+                              (size_t) sbuf.st_size);
+  if (NULL == ptr)
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+                              "mmap",
+                              filename);
+    GNUNET_DISK_file_close (fh);
+    return GNUNET_OK;
+  }
+  parse_key (filename,
+             ptr,
+             (size_t) sbuf.st_size);
+  GNUNET_DISK_file_unmap (map);
+  GNUNET_DISK_file_close (fh);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Load the various duration values from #kcfg.
+ *
+ * @return #GNUNET_OK on success
+ */
+static int
+load_durations (void)
+{
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_time (kcfg,
+                                           "taler-helper-crypto-eddsa",
+                                           "OVERLAP_DURATION",
+                                           &overlap_duration))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "taler-helper-crypto-eddsa",
+                               "OVERLAP_DURATION");
+    return GNUNET_SYSERR;
+  }
+  GNUNET_TIME_round_rel (&overlap_duration);
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_time (kcfg,
+                                           "taler-helper-crypto-eddsa",
+                                           "LOOKAHEAD_SIGN",
+                                           &lookahead_sign))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "taler-helper-crypto-eddsa",
+                               "LOOKAHEAD_SIGN");
+    return GNUNET_SYSERR;
+  }
+  GNUNET_TIME_round_rel (&lookahead_sign);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function run on shutdown. Stops the various jobs (nicely).
+ *
+ * @param cls NULL
+ */
+static void
+do_shutdown (void *cls)
+{
+  (void) cls;
+  if (NULL != read_task)
+  {
+    GNUNET_SCHEDULER_cancel (read_task);
+    read_task = NULL;
+  }
+  if (NULL != unix_sock)
+  {
+    GNUNET_break (GNUNET_OK ==
+                  GNUNET_NETWORK_socket_close (unix_sock));
+    unix_sock = NULL;
+  }
+  if (0 != unlink (unixpath))
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+                              "unlink",
+                              unixpath);
+  }
+  GNUNET_free (unixpath);
+  if (NULL != keygen_task)
+  {
+    GNUNET_SCHEDULER_cancel (keygen_task);
+    keygen_task = NULL;
+  }
+  if (NULL != done_task)
+  {
+    GNUNET_SCHEDULER_cancel (done_task);
+    done_task = NULL;
+  }
+  /* shut down worker threads */
+  GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
+  in_shutdown = true;
+  GNUNET_assert (0 == pthread_cond_broadcast (&work_cond));
+  GNUNET_assert (0 == pthread_mutex_unlock (&work_lock));
+  for (unsigned int i = 0; i<num_workers; i++)
+    GNUNET_assert (0 == pthread_join (workers[i],
+                                      NULL));
+  if (NULL != done_signal)
+  {
+    GNUNET_break (GNUNET_OK ==
+                  GNUNET_NETWORK_socket_close (done_signal));
+    done_signal = NULL;
+  }
+}
+
+
+/**
+ * Main function that will be run under the GNUnet scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be 
NULL!)
+ * @param cfg configuration
+ */
+static void
+run (void *cls,
+     char *const *args,
+     const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  (void) cls;
+  (void) args;
+  (void) cfgfile;
+  kcfg = cfg;
+  if (now.abs_value_us != now_tmp.abs_value_us)
+  {
+    /* The user gave "--now", use it! */
+    now = now_tmp;
+  }
+  else
+  {
+    /* get current time again, we may be timetraveling! */
+    now = GNUNET_TIME_absolute_get ();
+  }
+  GNUNET_TIME_round_abs (&now);
+  if (GNUNET_OK !=
+      load_durations ())
+  {
+    global_ret = 1;
+    return;
+  }
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (kcfg,
+                                               "taler-helper-crypto-eddsa",
+                                               "KEY_DIR",
+                                               &keydir))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "taler-helper-crypto-eddsa",
+                               "KEY_DIR");
+    global_ret = 1;
+    return;
+  }
+
+  /* open socket */
+  {
+    int sock;
+
+    sock = socket (PF_UNIX,
+                   SOCK_DGRAM,
+                   0);
+    if (-1 == sock)
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+                           "socket");
+      global_ret = 2;
+      return;
+    }
+    {
+      struct sockaddr_un un;
+
+      if (GNUNET_OK !=
+          GNUNET_CONFIGURATION_get_value_filename (kcfg,
+                                                   "taler-helper-crypto-eddsa",
+                                                   "UNIXPATH",
+                                                   &unixpath))
+      {
+        GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                                   "taler-helper-crypto-eddsa",
+                                   "UNIXPATH");
+        global_ret = 3;
+        return;
+      }
+      if (GNUNET_OK !=
+          GNUNET_DISK_directory_create_for_file (unixpath))
+      {
+        GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+                                  "mkdir(dirname)",
+                                  unixpath);
+      }
+      if (0 != unlink (unixpath))
+      {
+        if (ENOENT != errno)
+          GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+                                    "unlink",
+                                    unixpath);
+      }
+      memset (&un,
+              0,
+              sizeof (un));
+      un.sun_family = AF_UNIX;
+      strncpy (un.sun_path,
+               unixpath,
+               sizeof (un.sun_path));
+      if (0 != bind (sock,
+                     (const struct sockaddr *) &un,
+                     sizeof (un)))
+      {
+        GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                                  "bind",
+                                  unixpath);
+        global_ret = 3;
+        GNUNET_break (0 == close (sock));
+        return;
+      }
+    }
+    unix_sock = GNUNET_NETWORK_socket_box_native (sock);
+  }
+
+  GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
+                                 NULL);
+
+  /* Load keys */
+  GNUNET_break (GNUNET_OK ==
+                GNUNET_DISK_directory_create (keydir));
+  GNUNET_DISK_directory_scan (keydir,
+                              &import_key,
+                              NULL);
+  update_keys (NULL);
+  if (NULL == keys_head)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "No keys could be created. Strange.\n");
+    global_ret = 5;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+
+  /* start job to accept incoming requests on 'sock' */
+  read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                             unix_sock,
+                                             &read_job,
+                                             NULL);
+
+  /* start job to keep keys up-to-date */
+  keygen_task = GNUNET_SCHEDULER_add_now (&update_keys,
+                                          NULL);
+
+  /* start job to handle completed work */
+  {
+    int fd;
+
+    fd = eventfd (0,
+                  EFD_NONBLOCK | EFD_CLOEXEC);
+    if (-1 == fd)
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+                           "eventfd");
+      global_ret = 6;
+      GNUNET_SCHEDULER_shutdown ();
+      return;
+    }
+    done_signal = GNUNET_NETWORK_socket_box_native (fd);
+  }
+  done_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                             done_signal,
+                                             &handle_done,
+                                             NULL);
+
+  /* start crypto workers */
+  if (0 == num_workers)
+    num_workers = sysconf (_SC_NPROCESSORS_CONF);
+  workers = GNUNET_new_array (num_workers,
+                              pthread_t);
+  for (unsigned int i = 0; i<num_workers; i++)
+    GNUNET_assert (0 ==
+                   pthread_create (&workers[i],
+                                   NULL,
+                                   &sign_worker,
+                                   NULL));
+}
+
+
+/**
+ * The entry point.
+ *
+ * @param argc number of arguments in @a argv
+ * @param argv command-line arguments
+ * @return 0 on normal termination
+ */
+int
+main (int argc,
+      char **argv)
+{
+  struct GNUNET_GETOPT_CommandLineOption options[] = {
+    GNUNET_GETOPT_option_timetravel ('T',
+                                     "timetravel"),
+    GNUNET_GETOPT_option_uint ('p',
+                               "parallelism",
+                               "NUM_WORKERS",
+                               "number of worker threads to use",
+                               &num_workers),
+    GNUNET_GETOPT_option_absolute_time ('t',
+                                        "time",
+                                        "TIMESTAMP",
+                                        "pretend it is a different time for 
the update",
+                                        &now_tmp),
+    GNUNET_GETOPT_OPTION_END
+  };
+  int ret;
+
+  (void) umask (S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH);
+  /* force linker to link against libtalerutil; if we do
+   not do this, the linker may "optimize" libtalerutil
+   away and skip #TALER_OS_init(), which we do need */
+  GNUNET_OS_init (TALER_project_data_default ());
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_log_setup ("taler-helper-crypto-eddsa",
+                                   "WARNING",
+                                   NULL));
+  now = now_tmp = GNUNET_TIME_absolute_get ();
+  ret = GNUNET_PROGRAM_run (argc, argv,
+                            "taler-helper-crypto-eddsa",
+                            "Handle private EDDSA key operations for a Taler 
exchange",
+                            options,
+                            &run,
+                            NULL);
+  if (GNUNET_NO == ret)
+    return 0;
+  if (GNUNET_SYSERR == ret)
+    return 1;
+  return global_ret;
+}
diff --git a/src/util/taler-helper-crypto-eddsa.h 
b/src/util/taler-helper-crypto-eddsa.h
new file mode 100644
index 00000000..215af566
--- /dev/null
+++ b/src/util/taler-helper-crypto-eddsa.h
@@ -0,0 +1,188 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2020 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file util/taler-helper-crypto-eddsa.h
+ * @brief IPC messages for the EDDSA crypto helper.
+ * @author Christian Grothoff
+ */
+#ifndef TALER_HELPER_CRYPTO_EDDSA_H
+#define TALER_HELPER_CRYPTO_EDDSA_H
+
+#define TALER_HELPER_EDDSA_MT_PURGE 11
+#define TALER_HELPER_EDDSA_MT_AVAIL 12
+
+#define TALER_HELPER_EDDSA_MT_REQ_INIT 14
+#define TALER_HELPER_EDDSA_MT_REQ_SIGN 15
+#define TALER_HELPER_EDDSA_MT_REQ_REVOKE 16
+
+#define TALER_HELPER_EDDSA_MT_RES_SIGNATURE 17
+#define TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE 18
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Message sent if a key is available.
+ */
+struct TALER_CRYPTO_EddsaKeyAvailableNotification
+{
+  /**
+   * Type is #TALER_HELPER_EDDSA_MT_AVAIL
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * For now, always zero.
+   */
+  uint32_t reserved;
+
+  /**
+   * When does the key become available?
+   */
+  struct GNUNET_TIME_AbsoluteNBO anchor_time;
+
+  /**
+   * How long is the key available after @e anchor_time?
+   */
+  struct GNUNET_TIME_RelativeNBO duration;
+
+  /**
+   * The public key.
+   */
+  struct TALER_ExchangePublicKeyP exchange_pub;
+
+};
+
+
+/**
+ * Message sent if a key was purged.
+ */
+struct TALER_CRYPTO_EddsaKeyPurgeNotification
+{
+  /**
+   * Type is #TALER_HELPER_EDDSA_MT_PURGE.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * For now, always zero.
+   */
+  uint32_t reserved;
+
+  /**
+   * The public key.
+   */
+  struct TALER_ExchangePublicKeyP exchange_pub;
+
+};
+
+
+/**
+ * Message sent if a signature is requested.
+ */
+struct TALER_CRYPTO_SignRequest
+{
+  /**
+   * Type is #TALER_HELPER_EDDSA_MT_REQ_SIGN.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * For now, always zero.
+   */
+  uint32_t reserved;
+
+  /**
+   * What should be signed over.
+   */
+  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+  /* followed by rest of data to sign */
+};
+
+
+/**
+ * Message sent if a key was revoked.
+ */
+struct TALER_CRYPTO_RevokeRequest
+{
+  /**
+   * Type is #TALER_HELPER_EDDSA_MT_REQ_REVOKE.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * For now, always zero.
+   */
+  uint32_t reserved;
+
+  /**
+   * The public key to revoke.
+   */
+  struct TALER_ExchangePublicKeyP exchange_pub;
+
+};
+
+
+/**
+ * Message sent if a signature was successfully computed.
+ */
+struct TALER_CRYPTO_SignResponse
+{
+  /**
+   * Type is #TALER_HELPER_EDDSA_MT_RES_SIGNATURE.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * For now, always zero.
+   */
+  uint32_t reserved;
+
+  /**
+   * The public key used for the signature.
+   */
+  struct TALER_ExchangePublicKeyP exchange_pub;
+
+  /**
+   * The public key to use for the signature.
+   */
+  struct TALER_ExchangeSignatureP exchange_sig;
+
+};
+
+
+/**
+ * Message sent if signing failed.
+ */
+struct TALER_CRYPTO_SignFailure
+{
+  /**
+   * Type is #TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * If available, Taler error code. In NBO.
+   */
+  uint32_t ec;
+
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+
+#endif

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



reply via email to

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