/*
  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 
*/
/**
 * @file util/taler-exchange-secmod-eddsa.c
 * @brief Standalone process to perform private key EDDSA operations
 * @author Christian Grothoff
 *
 * 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-exchange-secmod-eddsa.h"
#include 
#include 
#include 
#include "taler_error_codes.h"
#include "taler_signatures.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;
};
/**
 * Private key of this security module. Used to sign denomination key
 * announcements.
 */
static struct TALER_SecurityModulePrivateKeyP smpriv;
/**
 * Public key of this security module.
 */
static struct TALER_SecurityModulePublicKeyP smpub;
/**
 * Head of DLL of actual keys, sorted by anchor.
 */
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 - #overlap_duration 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_workers sign_worker() threads.
 */
static pthread_t *workers;
/**
 * Main function of a worker thread that signs.
 *
 * @param cls NULL
 * @return NULL
 */
static void *
sign_worker (void *cls)
{
  (void) cls;
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Crypto worker launching\n");
  GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
  while (! in_shutdown)
  {
    struct WorkItem *wi;
    while (NULL != (wi = work_head))
    {
      /* take work from queue */
      GNUNET_CONTAINER_DLL_remove (work_head,
                                   work_tail,
                                   wi);
      work_counter--;
      GNUNET_assert (0 == pthread_mutex_unlock (&work_lock));
      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                  "Processing sign request %p\n",
                  wi);
      {
        if (GNUNET_OK !=
            GNUNET_CRYPTO_eddsa_sign_ (&wi->key->exchange_priv.eddsa_priv,
                                       wi->purpose,
                                       &wi->signature.eddsa_signature))
          wi->ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
        else
          wi->ec = TALER_EC_NONE;
      }
      /* put completed work into done queue */
      GNUNET_assert (0 == pthread_mutex_lock (&done_lock));
      GNUNET_CONTAINER_DLL_insert (done_head,
                                   done_tail,
                                   wi);
      GNUNET_assert (0 == pthread_mutex_unlock (&done_lock));
      {
        uint64_t val = GNUNET_htonll (1);
        /* raise #done_signal */
        if (sizeof(val) !=
            write (GNUNET_NETWORK_get_fd (done_signal),
                   &val,
                   sizeof (val)))
          GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
                               "write(eventfd)");
      }
      GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
    }
    if (in_shutdown)
      break;
    /* queue is empty, wait for work */
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Crypto worker waiting for work...\n");
    GNUNET_assert (0 ==
                   pthread_cond_wait (&work_cond,
                                      &work_lock));
  }
  GNUNET_assert (0 ==
                 pthread_mutex_unlock (&work_lock));
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Crypto worker exiting\n");
  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.  We expect that
 * the client is mostly able to handle everything at whatever speed
 * we have (after all, the crypto should be the slow part). However,
 * especially on startup when we send all of our keys, it is possible
 * that the client cannot keep up. In that case, we throttle when
 * sending fails. This does not work with poll() as we cannot specify
 * the sendto() target address with poll(). So we nanosleep() instead.
 *
 * @param addr address where to send the message
 * @param addr_size number of bytes in @a addr
 * @param hdr beginning of the message, length indicated in size field
 * @return #GNUNET_OK on success
 */
static int
transmit (const struct sockaddr_un *addr,
          socklen_t addr_size,
          const struct GNUNET_MessageHeader *hdr)
{
  for (unsigned int i = 0; i<100; i++)
  {
    ssize_t ret = sendto (GNUNET_NETWORK_get_fd (unix_sock),
                          hdr,
                          ntohs (hdr->size),
                          0 /* no flags => blocking! */,
                          (const struct sockaddr *) addr,
                          addr_size);
    if ( (-1 == ret) &&
         (EAGAIN == errno) )
    {
      /* _Maybe_ with blocking sendto(), this should no
         longer be needed; still keeping it just in case. */
      /* Wait a bit, in case client is just too slow */
      struct timespec req = {
        .tv_sec = 0,
        .tv_nsec = 1000
      };
      nanosleep (&req, NULL);
      continue;
    }
    if (ret == ntohs (hdr->size))
      return GNUNET_OK;
    if (ret != ntohs (hdr->size))
      break;
  }
  GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
                       "sendto");
  return GNUNET_SYSERR;
}
/**
 * Process completed tasks that are in the #done_head queue, sending
 * the result back to the client (and resuming the client).
 *
 * @param cls NULL
 */
static void
handle_done (void *cls)
{
  uint64_t data;
  (void) cls;
  /* consume #done_signal */
  if (sizeof (data) !=
      read (GNUNET_NETWORK_get_fd (done_signal),
            &data,
            sizeof (data)))
    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
                         "read(eventfd)");
  done_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
                                             done_signal,
                                             &handle_done,
                                             NULL);
  GNUNET_assert (0 == pthread_mutex_lock (&done_lock));
  while (NULL != done_head)
  {
    struct WorkItem *wi = done_head;
    GNUNET_CONTAINER_DLL_remove (done_head,
                                 done_tail,
                                 wi);
    GNUNET_assert (0 == pthread_mutex_unlock (&done_lock));
    if (TALER_EC_NONE != wi->ec)
    {
      struct TALER_CRYPTO_EddsaSignFailure sf = {
        .header.size = htons (sizeof (sf)),
        .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE),
        .ec = htonl (wi->ec)
      };
      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                  "Signing request %p failed, worker failed to produce signature\n",
                  wi);
      (void) transmit (&wi->addr,
                       wi->addr_size,
                       &sf.header);
    }
    else
    {
      struct TALER_CRYPTO_EddsaSignResponse sr = {
        .header.size = htons (sizeof (sr)),
        .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGNATURE),
        .exchange_pub = wi->key->exchange_pub,
        .exchange_sig = wi->signature
      };
      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                  "Transmitting signature for request %p\n",
                  wi);
      (void) transmit (&wi->addr,
                       wi->addr_size,
                       &sr.header);
    }
    {
      struct Key *key = wi->key;
      key->rc--;
      if ( (0 == key->rc) &&
           (key->purge) )
        free_key (key);
    }
    GNUNET_free (wi->purpose);
    GNUNET_free (wi);
    GNUNET_assert (0 == pthread_mutex_lock (&done_lock));
  }
  GNUNET_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_EddsaSignRequest *sr)
{
  const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose = &sr->purpose;
  struct WorkItem *wi;
  size_t purpose_size = ntohs (sr->header.size) - sizeof (*sr)
                        + sizeof (*purpose);
  if (purpose_size != htonl (purpose->size))
  {
    struct TALER_CRYPTO_EddsaSignFailure 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;
  }
  {
    struct GNUNET_TIME_Absolute now;
    now = GNUNET_TIME_absolute_get ();
    if ( (now.abs_value_us >= keys_head->anchor.abs_value_us) &&
         (now.abs_value_us < keys_head->anchor.abs_value_us
          + duration.rel_value_us) )
      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                  "Signing at %llu with key valid from %llu to %llu\n",
                  (unsigned long long) now.abs_value_us,
                  (unsigned long long) keys_head->anchor.abs_value_us,
                  (unsigned long long) keys_head->anchor.abs_value_us
                  + duration.rel_value_us);
    else
      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  "Signing at %llu with key valid from %llu to %llu\n",
                  (unsigned long long) now.abs_value_us,
                  (unsigned long long) keys_head->anchor.abs_value_us,
                  (unsigned long long) keys_head->anchor.abs_value_us
                  + duration.rel_value_us);
  }
  wi = GNUNET_new (struct WorkItem);
  wi->addr = *addr;
  wi->addr_size = addr_size;
  wi->key = keys_head;
  keys_head->rc++;
  wi->purpose = GNUNET_memdup (purpose,
                               purpose_size);
  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
              "Received request to sign over %u bytes, queueing as %p\n",
              (unsigned int) purpose_size,
              wi);
  GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
  work_counter++;
  GNUNET_CONTAINER_DLL_insert (work_head,
                               work_tail,
                               wi);
  GNUNET_assert (0 == pthread_cond_signal (&work_cond));
  GNUNET_assert (0 == pthread_mutex_unlock (&work_lock));
}
/**
 * Notify @a client about @a key becoming available.
 *
 * @param[in,out] client the client to notify; possible freed if transmission fails
 * @param key the key to notify @a client about
 * @return #GNUNET_OK on success
 */
static int
notify_client_key_add (struct Client *client,
                       const struct Key *key)
{
  struct TALER_CRYPTO_EddsaKeyAvailableNotification an = {
    .header.size = htons (sizeof (an)),
    .header.type = htons (TALER_HELPER_EDDSA_MT_AVAIL),
    .anchor_time = GNUNET_TIME_absolute_hton (key->anchor),
    .duration = GNUNET_TIME_relative_hton (duration),
    .exchange_pub = key->exchange_pub,
    .secm_pub = smpub
  };
  TALER_exchange_secmod_eddsa_sign (&key->exchange_pub,
                                    key->anchor,
                                    duration,
                                    &smpriv,
                                    &an.secm_sig);
  if (GNUNET_OK !=
      transmit (&client->addr,
                client->addr_size,
                &an.header))
  {
    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 (GNUNET_OK !=
      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_EddsaRevokeRequest *rr)
{
  struct Key *key;
  struct Key *nkey;
  key = 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 */
          client = NULL;
          break;
        }
      }
      if (NULL != client)
      {
        struct GNUNET_MessageHeader synced = {
          .type = htons (TALER_HELPER_EDDSA_SYNCED),
          .size = htons (sizeof (synced))
        };
        if (GNUNET_OK !=
            transmit (&client->addr,
                      client->addr_size,
                      &synced))
        {
          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                      "Client %s must have disconnected\n",
                      client->addr.sun_path);
          free_client (client);
        }
      }
    }
    break;
  case TALER_HELPER_EDDSA_MT_REQ_SIGN:
    if (ntohs (hdr->size) < sizeof (struct TALER_CRYPTO_EddsaSignRequest))
    {
      GNUNET_break_op (0);
      return;
    }
    handle_sign_request (&addr,
                         addr_size,
                         (const struct TALER_CRYPTO_EddsaSignRequest *) buf);
    break;
  case TALER_HELPER_EDDSA_MT_REQ_REVOKE:
    if (ntohs (hdr->size) != sizeof (struct TALER_CRYPTO_EddsaRevokeRequest))
    {
      GNUNET_break_op (0);
      return;
    }
    handle_revoke_request (&addr,
                           addr_size,
                           (const struct
                            TALER_CRYPTO_EddsaRevokeRequest *) 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_break (0);
    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) )
    if (GNUNET_OK !=
        create_key ())
    {
      GNUNET_break (0);
      GNUNET_SCHEDULER_shutdown ();
      return;
    }
  /* remove expired keys */
  while ( (NULL != keys_head) &&
          (0 ==
           GNUNET_TIME_absolute_get_remaining
             (GNUNET_TIME_absolute_add (keys_head->anchor,
                                        duration)).rel_value_us) )
    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-exchange-secmod-eddsa",
                                           "OVERLAP_DURATION",
                                           &overlap_duration))
  {
    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                               "taler-exchange-secmod-eddsa",
                               "OVERLAP_DURATION");
    return GNUNET_SYSERR;
  }
  if (GNUNET_OK !=
      GNUNET_CONFIGURATION_get_value_time (kcfg,
                                           "taler-exchange-secmod-eddsa",
                                           "DURATION",
                                           &duration))
  {
    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                               "taler-exchange-secmod-eddsa",
                               "DURATION");
    return GNUNET_SYSERR;
  }
  GNUNET_TIME_round_rel (&overlap_duration);
  if (GNUNET_OK !=
      GNUNET_CONFIGURATION_get_value_time (kcfg,
                                           "taler-exchange-secmod-eddsa",
                                           "LOOKAHEAD_SIGN",
                                           &lookahead_sign))
  {
    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                               "taler-exchange-secmod-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 */
  if (NULL != workers)
  {
    GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
    in_shutdown = true;
    GNUNET_assert (0 == pthread_cond_broadcast (&work_cond));
    GNUNET_assert (0 == pthread_mutex_unlock (&work_lock));
    for (unsigned int i = 0; i