exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

secmod_eddsa.c (32333B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2021 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file util/secmod_eddsa.c
     18  * @brief Standalone process to perform private key EDDSA operations
     19  * @author Christian Grothoff
     20  *
     21  * Key design points:
     22  * - EVERY thread of the exchange will have its own pair of connections to the
     23  *   crypto helpers.  This way, every threat will also have its own /keys state
     24  *   and avoid the need to synchronize on those.
     25  * - auditor signatures and master signatures are to be kept in the exchange DB,
     26  *   and merged with the public keys of the helper by the exchange HTTPD!
     27  * - the main loop of the helper is SINGLE-THREADED, but there are
     28  *   threads for crypto-workers which (only) do the signing in parallel,
     29  *   one per client.
     30  * - thread-safety: signing happens in parallel, thus when REMOVING private keys,
     31  *   we must ensure that all signers are done before we fully free() the
     32  *   private key. This is done by reference counting (as work is always
     33  *   assigned and collected by the main thread).
     34  */
     35 #include "platform.h"
     36 #include "taler/taler_util.h"
     37 #include "secmod_eddsa.h"
     38 #include <gcrypt.h>
     39 #include <pthread.h>
     40 #include "taler/taler_error_codes.h"
     41 #include "taler/taler_signatures.h"
     42 #include "secmod_common.h"
     43 #include <poll.h>
     44 
     45 
     46 /**
     47  * One particular key.
     48  */
     49 struct Key
     50 {
     51 
     52   /**
     53    * Kept in a DLL. Sorted by anchor time.
     54    */
     55   struct Key *next;
     56 
     57   /**
     58    * Kept in a DLL. Sorted by anchor time.
     59    */
     60   struct Key *prev;
     61 
     62   /**
     63    * Name of the file this key is stored under.
     64    */
     65   char *filename;
     66 
     67   /**
     68    * The private key.
     69    */
     70   struct TALER_ExchangePrivateKeyP exchange_priv;
     71 
     72   /**
     73    * The public key.
     74    */
     75   struct TALER_ExchangePublicKeyP exchange_pub;
     76 
     77   /**
     78    * Time at which this key is supposed to become valid.
     79    */
     80   struct GNUNET_TIME_Timestamp anchor;
     81 
     82   /**
     83    * Generation when this key was created or revoked.
     84    */
     85   uint64_t key_gen;
     86 
     87   /**
     88    * Reference counter. Counts the number of threads that are
     89    * using this key at this time.
     90    */
     91   unsigned int rc;
     92 
     93   /**
     94    * Flag set to true if this key has been purged and the memory
     95    * must be freed as soon as @e rc hits zero.
     96    */
     97   bool purge;
     98 
     99 };
    100 
    101 
    102 /**
    103  * Head of DLL of actual keys, sorted by anchor.
    104  */
    105 static struct Key *keys_head;
    106 
    107 /**
    108  * Tail of DLL of actual keys.
    109  */
    110 static struct Key *keys_tail;
    111 
    112 /**
    113  * How long can a key be used?
    114  */
    115 static struct GNUNET_TIME_Relative duration;
    116 
    117 /**
    118  * Command-line options for various TALER_SECMOD_XXX_run() functions.
    119  */
    120 static struct TALER_SECMOD_Options *globals;
    121 
    122 /**
    123  * Where do we store the keys?
    124  */
    125 static char *keydir;
    126 
    127 /**
    128  * How much should coin creation duration overlap
    129  * with the next key?  Basically, the starting time of two
    130  * keys is always #duration - #overlap_duration apart.
    131  */
    132 static struct GNUNET_TIME_Relative overlap_duration;
    133 
    134 /**
    135  * How long into the future do we pre-generate keys?
    136  */
    137 static struct GNUNET_TIME_Relative lookahead_sign;
    138 
    139 /**
    140  * Task run to generate new keys.
    141  */
    142 static struct GNUNET_SCHEDULER_Task *keygen_task;
    143 
    144 /**
    145  * Lock for the keys queue.
    146  */
    147 static pthread_mutex_t keys_lock = PTHREAD_MUTEX_INITIALIZER;
    148 
    149 /**
    150  * Current key generation.
    151  */
    152 static uint64_t key_gen;
    153 
    154 
    155 /**
    156  * Notify @a client about @a key becoming available.
    157  *
    158  * @param[in,out] client the client to notify; possible freed if transmission fails
    159  * @param key the key to notify @a client about
    160  * @return #GNUNET_OK on success
    161  */
    162 static enum GNUNET_GenericReturnValue
    163 notify_client_key_add (struct TES_Client *client,
    164                        const struct Key *key)
    165 {
    166   struct TALER_CRYPTO_EddsaKeyAvailableNotification an = {
    167     .header.size = htons (sizeof (an)),
    168     .header.type = htons (TALER_HELPER_EDDSA_MT_AVAIL),
    169     .anchor_time = GNUNET_TIME_timestamp_hton (key->anchor),
    170     .duration = GNUNET_TIME_relative_hton (duration),
    171     .exchange_pub = key->exchange_pub,
    172     .secm_pub = TES_smpub
    173   };
    174 
    175   TALER_exchange_secmod_eddsa_sign (&key->exchange_pub,
    176                                     key->anchor,
    177                                     duration,
    178                                     &TES_smpriv,
    179                                     &an.secm_sig);
    180   if (GNUNET_OK !=
    181       TES_transmit (client->csock,
    182                     &an.header))
    183   {
    184     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    185                 "Client %p must have disconnected\n",
    186                 client);
    187     return GNUNET_SYSERR;
    188   }
    189   return GNUNET_OK;
    190 }
    191 
    192 
    193 /**
    194  * Notify @a client about @a key being purged.
    195  *
    196  * @param[in,out] client the client to notify; possible freed if transmission fails
    197  * @param key the key to notify @a client about
    198  * @return #GNUNET_OK on success
    199  */
    200 static enum GNUNET_GenericReturnValue
    201 notify_client_key_del (struct TES_Client *client,
    202                        const struct Key *key)
    203 {
    204   struct TALER_CRYPTO_EddsaKeyPurgeNotification pn = {
    205     .header.type = htons (TALER_HELPER_EDDSA_MT_PURGE),
    206     .header.size = htons (sizeof (pn)),
    207     .exchange_pub = key->exchange_pub
    208   };
    209 
    210   if (GNUNET_OK !=
    211       TES_transmit (client->csock,
    212                     &pn.header))
    213   {
    214     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    215                 "Client %p must have disconnected\n",
    216                 client);
    217     return GNUNET_SYSERR;
    218   }
    219   return GNUNET_OK;
    220 }
    221 
    222 
    223 /**
    224  * Handle @a client request @a sr to create signature. Create the
    225  * signature using the respective key and return the result to
    226  * the client.
    227  *
    228  * @param client the client making the request
    229  * @param sr the request details
    230  * @return #GNUNET_OK on success
    231  */
    232 static enum GNUNET_GenericReturnValue
    233 handle_sign_request (struct TES_Client *client,
    234                      const struct TALER_CRYPTO_EddsaSignRequest *sr)
    235 {
    236   const struct GNUNET_CRYPTO_SignaturePurpose *purpose = &sr->purpose;
    237   size_t purpose_size = ntohs (sr->header.size) - sizeof (*sr)
    238                         + sizeof (*purpose);
    239   struct Key *key;
    240   struct TALER_CRYPTO_EddsaSignResponse sres = {
    241     .header.size = htons (sizeof (sres)),
    242     .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGNATURE)
    243   };
    244   enum TALER_ErrorCode ec;
    245 
    246   if (purpose_size != ntohl (purpose->size))
    247   {
    248     struct TALER_CRYPTO_EddsaSignFailure sf = {
    249       .header.size = htons (sizeof (sf)),
    250       .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE),
    251       .ec = htonl (TALER_EC_GENERIC_PARAMETER_MALFORMED)
    252     };
    253 
    254     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    255                 "Signing request failed, request malformed\n");
    256     return TES_transmit (client->csock,
    257                          &sf.header);
    258   }
    259 
    260   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    261   key = keys_head;
    262   while ( (NULL != key) &&
    263           (GNUNET_TIME_absolute_is_past (
    264              GNUNET_TIME_absolute_add (key->anchor.abs_time,
    265                                        duration))) )
    266   {
    267     struct Key *nxt = key->next;
    268 
    269     if (0 != key->rc)
    270       break; /* do later */
    271     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    272                 "Deleting past key %s (expired %s ago)\n",
    273                 TALER_B2S (&key->exchange_pub),
    274                 GNUNET_TIME_relative2s (
    275                   GNUNET_TIME_absolute_get_duration (
    276                     GNUNET_TIME_absolute_add (key->anchor.abs_time,
    277                                               duration)),
    278                   GNUNET_YES));
    279     GNUNET_CONTAINER_DLL_remove (keys_head,
    280                                  keys_tail,
    281                                  key);
    282     if ( (! key->purge) &&
    283          (0 != unlink (key->filename)) )
    284       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    285                                 "unlink",
    286                                 key->filename);
    287     GNUNET_free (key->filename);
    288     GNUNET_free (key);
    289     key = nxt;
    290   }
    291   /* revoked keys must not be used for signing anymore; the replacement
    292      key sits right behind the revoked one in the DLL */
    293   while ( (NULL != key) &&
    294           (key->purge) )
    295     key = key->next;
    296   if (NULL == key)
    297   {
    298     GNUNET_break (0);
    299     ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
    300   }
    301   else
    302   {
    303     GNUNET_assert (key->rc < UINT_MAX);
    304     key->rc++;
    305     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    306 
    307     if (GNUNET_OK !=
    308         GNUNET_CRYPTO_eddsa_sign_ (&key->exchange_priv.eddsa_priv,
    309                                    purpose,
    310                                    &sres.exchange_sig.eddsa_signature))
    311       ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    312     else
    313       ec = TALER_EC_NONE;
    314     sres.exchange_pub = key->exchange_pub;
    315     GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    316     GNUNET_assert (key->rc > 0);
    317     key->rc--;
    318   }
    319   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    320   if (TALER_EC_NONE != ec)
    321   {
    322     struct TALER_CRYPTO_EddsaSignFailure sf = {
    323       .header.size = htons (sizeof (sf)),
    324       .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE),
    325       .ec = htonl ((uint32_t) ec)
    326     };
    327 
    328     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    329                 "Signing request %p failed, worker failed to produce signature\n",
    330                 client);
    331     return TES_transmit (client->csock,
    332                          &sf.header);
    333   }
    334   return TES_transmit (client->csock,
    335                        &sres.header);
    336 }
    337 
    338 
    339 /**
    340  * Initialize key material for key @a key (also on disk).
    341  *
    342  * @param[in,out] key to compute key material for
    343  * @param position where in the DLL will the @a key go
    344  * @return #GNUNET_OK on success
    345  */
    346 static enum GNUNET_GenericReturnValue
    347 setup_key (struct Key *key,
    348            struct Key *position)
    349 {
    350   struct GNUNET_CRYPTO_EddsaPrivateKey priv;
    351   struct GNUNET_CRYPTO_EddsaPublicKey pub;
    352 
    353   GNUNET_CRYPTO_eddsa_key_create (&priv);
    354   GNUNET_CRYPTO_eddsa_key_get_public (&priv,
    355                                       &pub);
    356   GNUNET_asprintf (&key->filename,
    357                    "%s/%llu",
    358                    keydir,
    359                    (unsigned long long) (key->anchor.abs_time.abs_value_us
    360                                          / GNUNET_TIME_UNIT_SECONDS.rel_value_us
    361                                          ));
    362   if (GNUNET_OK !=
    363       GNUNET_DISK_fn_write (key->filename,
    364                             &priv,
    365                             sizeof (priv),
    366                             GNUNET_DISK_PERM_USER_READ))
    367   {
    368     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    369                               "write",
    370                               key->filename);
    371     return GNUNET_SYSERR;
    372   }
    373   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    374               "Setup fresh private key in `%s'\n",
    375               key->filename);
    376   key->key_gen = key_gen;
    377   key->exchange_priv.eddsa_priv = priv;
    378   key->exchange_pub.eddsa_pub = pub;
    379   GNUNET_CONTAINER_DLL_insert_after (keys_head,
    380                                      keys_tail,
    381                                      position,
    382                                      key);
    383   return GNUNET_OK;
    384 }
    385 
    386 
    387 /**
    388  * The validity period of a key @a key has expired. Purge it.
    389  *
    390  * @param[in] key expired or revoked key to purge
    391  */
    392 static void
    393 purge_key (struct Key *key)
    394 {
    395   if (key->purge)
    396   {
    397     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    398                 "Key %s already purged, skipping\n",
    399                 TALER_B2S (&key->exchange_pub));
    400     return;
    401   }
    402   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    403               "Purging key %s\n",
    404               TALER_B2S (&key->exchange_pub));
    405   if (0 != unlink (key->filename))
    406     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    407                               "unlink",
    408                               key->filename);
    409   key->purge = true;
    410   key->key_gen = key_gen;
    411   GNUNET_free (key->filename);
    412 }
    413 
    414 
    415 /**
    416  * A @a client informs us that a key has been revoked.
    417  * Check if the key is still in use, and if so replace (!)
    418  * it with a fresh key.
    419  *
    420  * @param client the client making the request
    421  * @param rr the revocation request
    422  * @return #GNUNET_OK on success
    423  */
    424 static enum GNUNET_GenericReturnValue
    425 handle_revoke_request (struct TES_Client *client,
    426                        const struct TALER_CRYPTO_EddsaRevokeRequest *rr)
    427 {
    428   struct Key *key;
    429   struct Key *nkey;
    430 
    431   (void) client;
    432   key = NULL;
    433   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    434   for (struct Key *pos = keys_head;
    435        NULL != pos;
    436        pos = pos->next)
    437     if (0 == GNUNET_memcmp (&pos->exchange_pub,
    438                             &rr->exchange_pub))
    439     {
    440       key = pos;
    441       break;
    442     }
    443   if (NULL == key)
    444   {
    445     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    446     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    447                 "Revocation request ignored, key unknown\n");
    448     return GNUNET_OK;
    449   }
    450   if (key->purge)
    451   {
    452     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    453     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    454                 "Revocation request ignored, key %s already revoked\n",
    455                 TALER_B2S (&key->exchange_pub));
    456     return GNUNET_OK;
    457   }
    458   key_gen++;
    459   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    460               "Revoking key %s, bumping generation to %llu\n",
    461               TALER_B2S (&key->exchange_pub),
    462               (unsigned long long) key_gen);
    463   purge_key (key);
    464 
    465   /* Setup replacement key */
    466   nkey = GNUNET_new (struct Key);
    467   nkey->anchor = key->anchor;
    468   if (GNUNET_OK !=
    469       setup_key (nkey,
    470                  key))
    471   {
    472     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    473     GNUNET_break (0);
    474     GNUNET_SCHEDULER_shutdown ();
    475     globals->global_ret = EXIT_FAILURE;
    476     return GNUNET_SYSERR;
    477   }
    478   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    479   TES_wake_clients ();
    480   return GNUNET_OK;
    481 }
    482 
    483 
    484 /**
    485  * Handle @a hdr message received from @a client.
    486  *
    487  * @param client the client that received the message
    488  * @param hdr message that was received
    489  * @return #GNUNET_OK on success
    490  */
    491 static enum GNUNET_GenericReturnValue
    492 eddsa_work_dispatch (struct TES_Client *client,
    493                      const struct GNUNET_MessageHeader *hdr)
    494 {
    495   uint16_t msize = ntohs (hdr->size);
    496 
    497   switch (ntohs (hdr->type))
    498   {
    499   case TALER_HELPER_EDDSA_MT_REQ_SIGN:
    500     if (msize < sizeof (struct TALER_CRYPTO_EddsaSignRequest))
    501     {
    502       GNUNET_break_op (0);
    503       return GNUNET_SYSERR;
    504     }
    505     return handle_sign_request (
    506       client,
    507       (const struct TALER_CRYPTO_EddsaSignRequest *) hdr);
    508   case TALER_HELPER_EDDSA_MT_REQ_REVOKE:
    509     if (msize != sizeof (struct TALER_CRYPTO_EddsaRevokeRequest))
    510     {
    511       GNUNET_break_op (0);
    512       return GNUNET_SYSERR;
    513     }
    514     return handle_revoke_request (
    515       client,
    516       (const struct TALER_CRYPTO_EddsaRevokeRequest *) hdr);
    517   default:
    518     GNUNET_break_op (0);
    519     return GNUNET_SYSERR;
    520   }
    521 }
    522 
    523 
    524 /**
    525  * Send our initial key set to @a client together with the
    526  * "sync" terminator.
    527  *
    528  * @param client the client to inform
    529  * @return #GNUNET_OK on success
    530  */
    531 static enum GNUNET_GenericReturnValue
    532 eddsa_client_init (struct TES_Client *client)
    533 {
    534   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    535   for (struct Key *key = keys_head;
    536        NULL != key;
    537        key = key->next)
    538   {
    539     if (GNUNET_OK !=
    540         notify_client_key_add (client,
    541                                key))
    542     {
    543       GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    544       GNUNET_break (0);
    545       return GNUNET_SYSERR;
    546     }
    547   }
    548   client->key_gen = key_gen;
    549   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    550   {
    551     struct GNUNET_MessageHeader synced = {
    552       .type = htons (TALER_HELPER_EDDSA_SYNCED),
    553       .size = htons (sizeof (synced))
    554     };
    555 
    556     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    557                 "Client %p synced\n",
    558                 client);
    559     if (GNUNET_OK !=
    560         TES_transmit (client->csock,
    561                       &synced))
    562     {
    563       GNUNET_break (0);
    564       return GNUNET_SYSERR;
    565     }
    566   }
    567   return GNUNET_OK;
    568 }
    569 
    570 
    571 /**
    572  * Notify @a client about all changes to the keys since
    573  * the last generation known to the @a client.
    574  *
    575  * @param client the client to notify
    576  * @return #GNUNET_OK on success
    577  */
    578 static enum GNUNET_GenericReturnValue
    579 eddsa_update_client_keys (struct TES_Client *client)
    580 {
    581   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    582   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    583               "Updating client %p to generation %llu\n",
    584               client,
    585               (unsigned long long) key_gen);
    586   for (struct Key *key = keys_head;
    587        NULL != key;
    588        key = key->next)
    589   {
    590     if (key->key_gen <= client->key_gen)
    591     {
    592       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    593                   "Skipping key %s, no change since generation %llu\n",
    594                   TALER_B2S (&key->exchange_pub),
    595                   (unsigned long long) client->key_gen);
    596       continue;
    597     }
    598     if (key->purge)
    599     {
    600       if (GNUNET_OK !=
    601           notify_client_key_del (client,
    602                                  key))
    603       {
    604         GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    605         return GNUNET_SYSERR;
    606       }
    607     }
    608     else
    609     {
    610       if (GNUNET_OK !=
    611           notify_client_key_add (client,
    612                                  key))
    613       {
    614         GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    615         return GNUNET_SYSERR;
    616       }
    617     }
    618   }
    619   client->key_gen = key_gen;
    620   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    621   return GNUNET_OK;
    622 }
    623 
    624 
    625 /**
    626  * Create a new key (we do not have enough).
    627  *
    628  * @return #GNUNET_OK on success
    629  */
    630 static enum GNUNET_GenericReturnValue
    631 create_key (void)
    632 {
    633   struct Key *key;
    634   struct GNUNET_TIME_Timestamp anchor;
    635 
    636   anchor = GNUNET_TIME_timestamp_get ();
    637   if (NULL != keys_tail)
    638   {
    639     struct GNUNET_TIME_Absolute abs;
    640 
    641     abs = GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time,
    642                                     GNUNET_TIME_relative_subtract (
    643                                       duration,
    644                                       overlap_duration));
    645     if (GNUNET_TIME_absolute_cmp (anchor.abs_time,
    646                                   <,
    647                                   abs))
    648       anchor = GNUNET_TIME_absolute_to_timestamp (abs);
    649   }
    650   key = GNUNET_new (struct Key);
    651   key->anchor = anchor;
    652   if (GNUNET_OK !=
    653       setup_key (key,
    654                  keys_tail))
    655   {
    656     GNUNET_break (0);
    657     GNUNET_free (key);
    658     GNUNET_SCHEDULER_shutdown ();
    659     globals->global_ret = EXIT_FAILURE;
    660     return GNUNET_SYSERR;
    661   }
    662   return GNUNET_OK;
    663 }
    664 
    665 
    666 /**
    667  * At what time does the current key set require its next action?  Basically,
    668  * the minimum of the expiration time of the oldest key, and the expiration
    669  * time of the newest key minus the #lookahead_sign time.
    670  *
    671  * Must only be called while the #keys_lock is held.
    672  */
    673 static struct GNUNET_TIME_Absolute
    674 key_action_time (void)
    675 {
    676   struct Key *nxt;
    677 
    678   nxt = keys_head;
    679   while ( (NULL != nxt) &&
    680           (nxt->purge) )
    681     nxt = nxt->next;
    682   if (NULL == nxt)
    683     return GNUNET_TIME_UNIT_ZERO_ABS;
    684   return GNUNET_TIME_absolute_min (
    685     GNUNET_TIME_absolute_add (nxt->anchor.abs_time,
    686                               duration),
    687     GNUNET_TIME_absolute_subtract (
    688       GNUNET_TIME_absolute_subtract (
    689         GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time,
    690                                   duration),
    691         lookahead_sign),
    692       overlap_duration));
    693 }
    694 
    695 
    696 /**
    697  * Create new keys and expire ancient keys.
    698  *
    699  * @param cls NULL
    700  */
    701 static void
    702 update_keys (void *cls)
    703 {
    704   bool wake = false;
    705   struct Key *nxt;
    706   struct GNUNET_TIME_Absolute at;
    707 
    708   (void) cls;
    709   keygen_task = NULL;
    710   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    711   /* create new keys */
    712   while ( (NULL == keys_tail) ||
    713           GNUNET_TIME_absolute_is_past (
    714             GNUNET_TIME_absolute_subtract (
    715               GNUNET_TIME_absolute_subtract (
    716                 GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time,
    717                                           duration),
    718                 lookahead_sign),
    719               overlap_duration)) )
    720   {
    721     if (! wake)
    722     {
    723       key_gen++;
    724       wake = true;
    725     }
    726     if (GNUNET_OK !=
    727         create_key ())
    728     {
    729       GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    730       GNUNET_break (0);
    731       globals->global_ret = EXIT_FAILURE;
    732       GNUNET_SCHEDULER_shutdown ();
    733       return;
    734     }
    735   }
    736   nxt = keys_head;
    737   /* purge expired keys */
    738   while ( (NULL != nxt) &&
    739           GNUNET_TIME_absolute_is_past (
    740             GNUNET_TIME_absolute_add (nxt->anchor.abs_time,
    741                                       duration)))
    742   {
    743     if (! wake)
    744     {
    745       key_gen++;
    746       wake = true;
    747     }
    748     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    749                 "Purging past key %s (expired %s ago)\n",
    750                 TALER_B2S (&nxt->exchange_pub),
    751                 GNUNET_TIME_relative2s (
    752                   GNUNET_TIME_absolute_get_duration (
    753                     GNUNET_TIME_absolute_add (nxt->anchor.abs_time,
    754                                               duration)),
    755                   GNUNET_YES));
    756     purge_key (nxt);
    757     nxt = nxt->next;
    758   }
    759   at = key_action_time ();
    760   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    761   if (wake)
    762     TES_wake_clients ();
    763   keygen_task = GNUNET_SCHEDULER_add_at (at,
    764                                          &update_keys,
    765                                          NULL);
    766 }
    767 
    768 
    769 /**
    770  * Parse private key from @a filename in @a buf.
    771  *
    772  * @param filename name of the file we are parsing, for logging
    773  * @param buf key material
    774  * @param buf_size number of bytes in @a buf
    775  * @return #GNUNET_OK on success
    776  */
    777 static enum GNUNET_GenericReturnValue
    778 parse_key (const char *filename,
    779            const void *buf,
    780            size_t buf_size)
    781 {
    782   struct GNUNET_CRYPTO_EddsaPrivateKey priv;
    783   char *anchor_s;
    784   char dummy;
    785   unsigned long long anchor_ll;
    786   struct GNUNET_TIME_Timestamp anchor;
    787 
    788   anchor_s = strrchr (filename,
    789                       '/');
    790   if (NULL == anchor_s)
    791   {
    792     /* File in a directory without '/' in the name, this makes no sense. */
    793     GNUNET_break (0);
    794     return GNUNET_SYSERR;
    795   }
    796   anchor_s++;
    797   if (1 != sscanf (anchor_s,
    798                    "%llu%c",
    799                    &anchor_ll,
    800                    &dummy))
    801   {
    802     /* Filenames in KEYDIR must ONLY be the anchor time in seconds! */
    803     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    804                 "Filename `%s' invalid for key file, skipping\n",
    805                 filename);
    806     return GNUNET_SYSERR;
    807   }
    808   anchor.abs_time.abs_value_us = anchor_ll
    809                                  * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
    810   if (anchor_ll != anchor.abs_time.abs_value_us
    811       / GNUNET_TIME_UNIT_SECONDS.rel_value_us)
    812   {
    813     /* Integer overflow. Bad, invalid filename. */
    814     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    815                 "Filename `%s' invalid for key file, skipping\n",
    816                 filename);
    817     return GNUNET_SYSERR;
    818   }
    819   if (buf_size != sizeof (priv))
    820   {
    821     /* Parser failure. */
    822     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    823                 "File `%s' is malformed, skipping\n",
    824                 filename);
    825     return GNUNET_SYSERR;
    826   }
    827   GNUNET_memcpy (&priv,
    828                  buf,
    829                  buf_size);
    830 
    831   {
    832     struct GNUNET_CRYPTO_EddsaPublicKey pub;
    833     struct Key *key;
    834     struct Key *before;
    835 
    836     GNUNET_CRYPTO_eddsa_key_get_public (&priv,
    837                                         &pub);
    838     GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    839     key = GNUNET_new (struct Key);
    840     key->exchange_priv.eddsa_priv = priv;
    841     key->exchange_pub.eddsa_pub = pub;
    842     key->anchor = anchor;
    843     key->filename = GNUNET_strdup (filename);
    844     key->key_gen = key_gen;
    845     before = NULL;
    846     for (struct Key *pos = keys_head;
    847          NULL != pos;
    848          pos = pos->next)
    849     {
    850       if (GNUNET_TIME_timestamp_cmp (pos->anchor, >, anchor))
    851         break;
    852       before = pos;
    853     }
    854     GNUNET_CONTAINER_DLL_insert_after (keys_head,
    855                                        keys_tail,
    856                                        before,
    857                                        key);
    858     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    859     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    860                 "Imported key from `%s'\n",
    861                 filename);
    862   }
    863   return GNUNET_OK;
    864 }
    865 
    866 
    867 /**
    868  * Import a private key from @a filename.
    869  *
    870  * @param cls NULL
    871  * @param filename name of a file in the directory
    872  */
    873 static enum GNUNET_GenericReturnValue
    874 import_key (void *cls,
    875             const char *filename)
    876 {
    877   struct GNUNET_DISK_FileHandle *fh;
    878   struct GNUNET_DISK_MapHandle *map;
    879   void *ptr;
    880   int fd;
    881   struct stat sbuf;
    882 
    883   (void) cls;
    884   {
    885     struct stat lsbuf;
    886 
    887     if (0 != lstat (filename,
    888                     &lsbuf))
    889     {
    890       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    891                                 "lstat",
    892                                 filename);
    893       return GNUNET_OK;
    894     }
    895     if (! S_ISREG (lsbuf.st_mode))
    896     {
    897       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    898                   "File `%s' is not a regular file, which is not allowed for private keys!\n",
    899                   filename);
    900       return GNUNET_OK;
    901     }
    902   }
    903 
    904   fd = open (filename,
    905              O_RDONLY | O_CLOEXEC);
    906   if (-1 == fd)
    907   {
    908     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    909                               "open",
    910                               filename);
    911     return GNUNET_OK;
    912   }
    913   if (0 != fstat (fd,
    914                   &sbuf))
    915   {
    916     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    917                               "stat",
    918                               filename);
    919     GNUNET_break (0 == close (fd));
    920     return GNUNET_OK;
    921   }
    922   if (! S_ISREG (sbuf.st_mode))
    923   {
    924     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    925                 "File `%s' is not a regular file, which is not allowed for private keys!\n",
    926                 filename);
    927     GNUNET_break (0 == close (fd));
    928     return GNUNET_OK;
    929   }
    930   if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO)))
    931   {
    932     /* permission are NOT tight, try to patch them up! */
    933     if (0 !=
    934         fchmod (fd,
    935                 S_IRUSR))
    936     {
    937       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    938                                 "fchmod",
    939                                 filename);
    940       /* refuse to use key if file has wrong permissions */
    941       GNUNET_break (0 == close (fd));
    942       return GNUNET_OK;
    943     }
    944   }
    945   fh = GNUNET_DISK_get_handle_from_int_fd (fd);
    946   if (NULL == fh)
    947   {
    948     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    949                               "open",
    950                               filename);
    951     GNUNET_break (0 == close (fd));
    952     return GNUNET_OK;
    953   }
    954   if (sbuf.st_size > 2048)
    955   {
    956     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    957                 "File `%s' to big to be a private key\n",
    958                 filename);
    959     GNUNET_DISK_file_close (fh);
    960     return GNUNET_OK;
    961   }
    962   ptr = GNUNET_DISK_file_map (fh,
    963                               &map,
    964                               GNUNET_DISK_MAP_TYPE_READ,
    965                               (size_t) sbuf.st_size);
    966   if (NULL == ptr)
    967   {
    968     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    969                               "mmap",
    970                               filename);
    971     GNUNET_DISK_file_close (fh);
    972     return GNUNET_OK;
    973   }
    974   (void) parse_key (filename,
    975                     ptr,
    976                     (size_t) sbuf.st_size);
    977   GNUNET_DISK_file_unmap (map);
    978   GNUNET_DISK_file_close (fh);
    979   return GNUNET_OK;
    980 }
    981 
    982 
    983 /**
    984  * Load the various duration values from @a kcfg.
    985  *
    986  * @param cfg configuration to use
    987  * @return #GNUNET_OK on success
    988  */
    989 static enum GNUNET_GenericReturnValue
    990 load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg)
    991 {
    992   char *secname;
    993 
    994   GNUNET_asprintf (&secname,
    995                    "%s-secmod-eddsa",
    996                    globals->section);
    997   if (GNUNET_OK !=
    998       GNUNET_CONFIGURATION_get_value_time (cfg,
    999                                            secname,
   1000                                            "OVERLAP_DURATION",
   1001                                            &overlap_duration))
   1002   {
   1003     GNUNET_free (secname);
   1004     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1005                                secname,
   1006                                "OVERLAP_DURATION");
   1007     return GNUNET_SYSERR;
   1008   }
   1009   if (GNUNET_OK !=
   1010       GNUNET_CONFIGURATION_get_value_time (cfg,
   1011                                            secname,
   1012                                            "DURATION",
   1013                                            &duration))
   1014   {
   1015     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1016                                secname,
   1017                                "DURATION");
   1018     GNUNET_free (secname);
   1019     return GNUNET_SYSERR;
   1020   }
   1021   if (GNUNET_OK !=
   1022       GNUNET_CONFIGURATION_get_value_time (cfg,
   1023                                            secname,
   1024                                            "LOOKAHEAD_SIGN",
   1025                                            &lookahead_sign))
   1026   {
   1027     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1028                                secname,
   1029                                "LOOKAHEAD_SIGN");
   1030     GNUNET_free (secname);
   1031     return GNUNET_SYSERR;
   1032   }
   1033   GNUNET_free (secname);
   1034   return GNUNET_OK;
   1035 }
   1036 
   1037 
   1038 /**
   1039  * Function run on shutdown. Stops the various jobs (nicely).
   1040  *
   1041  * @param cls NULL
   1042  */
   1043 static void
   1044 do_shutdown (void *cls)
   1045 {
   1046   (void) cls;
   1047   TES_listen_stop ();
   1048   if (NULL != keygen_task)
   1049   {
   1050     GNUNET_SCHEDULER_cancel (keygen_task);
   1051     keygen_task = NULL;
   1052   }
   1053 }
   1054 
   1055 
   1056 void
   1057 TALER_SECMOD_eddsa_run (void *cls,
   1058                         char *const *args,
   1059                         const char *cfgfile,
   1060                         const struct GNUNET_CONFIGURATION_Handle *cfg)
   1061 {
   1062   static struct TES_Callbacks cb = {
   1063     .dispatch = eddsa_work_dispatch,
   1064     .updater = eddsa_update_client_keys,
   1065     .init = eddsa_client_init
   1066   };
   1067   struct TALER_SECMOD_Options *opt = cls;
   1068   char *secname;
   1069 
   1070   (void) args;
   1071   (void) cfgfile;
   1072   globals = opt;
   1073   if (GNUNET_TIME_timestamp_cmp (opt->global_now,
   1074                                  !=,
   1075                                  opt->global_now_tmp))
   1076   {
   1077     /* The user gave "--now", use it! */
   1078     opt->global_now = opt->global_now_tmp;
   1079   }
   1080   else
   1081   {
   1082     /* get current time again, we may be timetraveling! */
   1083     opt->global_now = GNUNET_TIME_timestamp_get ();
   1084   }
   1085   if (GNUNET_OK !=
   1086       load_durations (cfg))
   1087   {
   1088     opt->global_ret = EXIT_NOTCONFIGURED;
   1089     return;
   1090   }
   1091   GNUNET_asprintf (&secname,
   1092                    "%s-secmod-eddsa",
   1093                    opt->section);
   1094   if (GNUNET_OK !=
   1095       GNUNET_CONFIGURATION_get_value_filename (cfg,
   1096                                                secname,
   1097                                                "KEY_DIR",
   1098                                                &keydir))
   1099   {
   1100     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1101                                secname,
   1102                                "KEY_DIR");
   1103     GNUNET_free (secname);
   1104     opt->global_ret = EXIT_NOTCONFIGURED;
   1105     return;
   1106   }
   1107   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
   1108                                  NULL);
   1109   opt->global_ret = TES_listen_start (cfg,
   1110                                       secname,
   1111                                       &cb);
   1112   GNUNET_free (secname);
   1113   if (0 != opt->global_ret)
   1114     return;
   1115   /* Load keys */
   1116   GNUNET_break (GNUNET_OK ==
   1117                 GNUNET_DISK_directory_create (keydir));
   1118   GNUNET_DISK_directory_scan (keydir,
   1119                               &import_key,
   1120                               NULL);
   1121   if ( (NULL != keys_head) &&
   1122        (GNUNET_TIME_absolute_is_future (keys_head->anchor.abs_time)) )
   1123   {
   1124     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1125                 "Existing anchor is in %s the future. Refusing to start\n",
   1126                 GNUNET_TIME_relative2s (
   1127                   GNUNET_TIME_absolute_get_remaining (
   1128                     keys_head->anchor.abs_time),
   1129                   true));
   1130     opt->global_ret = EXIT_FAILURE;
   1131     GNUNET_SCHEDULER_shutdown ();
   1132     return;
   1133   }
   1134   /* start job to keep keys up-to-date; MUST be run before the #listen_task,
   1135      hence with priority. */
   1136   keygen_task = GNUNET_SCHEDULER_add_with_priority (
   1137     GNUNET_SCHEDULER_PRIORITY_URGENT,
   1138     &update_keys,
   1139     NULL);
   1140 }