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 (32008B)


      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 "taler/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;
    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 != htonl (purpose->size))
    247   {
    248     struct TALER_CRYPTO_EddsaSignFailure sf = {
    249       .header.size = htons (sizeof (sr)),
    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 (&nxt->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   if (NULL == key)
    292   {
    293     GNUNET_break (0);
    294     ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
    295   }
    296   else
    297   {
    298     GNUNET_assert (key->rc < UINT_MAX);
    299     key->rc++;
    300     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    301 
    302     if (GNUNET_OK !=
    303         GNUNET_CRYPTO_eddsa_sign_ (&key->exchange_priv.eddsa_priv,
    304                                    purpose,
    305                                    &sres.exchange_sig.eddsa_signature))
    306       ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    307     else
    308       ec = TALER_EC_NONE;
    309     sres.exchange_pub = key->exchange_pub;
    310     GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    311     GNUNET_assert (key->rc > 0);
    312     key->rc--;
    313   }
    314   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    315   if (TALER_EC_NONE != ec)
    316   {
    317     struct TALER_CRYPTO_EddsaSignFailure sf = {
    318       .header.size = htons (sizeof (sf)),
    319       .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE),
    320       .ec = htonl ((uint32_t) ec)
    321     };
    322 
    323     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    324                 "Signing request %p failed, worker failed to produce signature\n",
    325                 client);
    326     return TES_transmit (client->csock,
    327                          &sf.header);
    328   }
    329   return TES_transmit (client->csock,
    330                        &sres.header);
    331 }
    332 
    333 
    334 /**
    335  * Initialize key material for key @a key (also on disk).
    336  *
    337  * @param[in,out] key to compute key material for
    338  * @param position where in the DLL will the @a key go
    339  * @return #GNUNET_OK on success
    340  */
    341 static enum GNUNET_GenericReturnValue
    342 setup_key (struct Key *key,
    343            struct Key *position)
    344 {
    345   struct GNUNET_CRYPTO_EddsaPrivateKey priv;
    346   struct GNUNET_CRYPTO_EddsaPublicKey pub;
    347 
    348   GNUNET_CRYPTO_eddsa_key_create (&priv);
    349   GNUNET_CRYPTO_eddsa_key_get_public (&priv,
    350                                       &pub);
    351   GNUNET_asprintf (&key->filename,
    352                    "%s/%llu",
    353                    keydir,
    354                    (unsigned long long) (key->anchor.abs_time.abs_value_us
    355                                          / GNUNET_TIME_UNIT_SECONDS.rel_value_us
    356                                          ));
    357   if (GNUNET_OK !=
    358       GNUNET_DISK_fn_write (key->filename,
    359                             &priv,
    360                             sizeof (priv),
    361                             GNUNET_DISK_PERM_USER_READ))
    362   {
    363     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    364                               "write",
    365                               key->filename);
    366     return GNUNET_SYSERR;
    367   }
    368   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    369               "Setup fresh private key in `%s'\n",
    370               key->filename);
    371   key->key_gen = key_gen;
    372   key->exchange_priv.eddsa_priv = priv;
    373   key->exchange_pub.eddsa_pub = pub;
    374   GNUNET_CONTAINER_DLL_insert_after (keys_head,
    375                                      keys_tail,
    376                                      position,
    377                                      key);
    378   return GNUNET_OK;
    379 }
    380 
    381 
    382 /**
    383  * The validity period of a key @a key has expired. Purge it.
    384  *
    385  * @param[in] key expired or revoked key to purge
    386  */
    387 static void
    388 purge_key (struct Key *key)
    389 {
    390   if (key->purge)
    391   {
    392     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    393                 "Key %s already purged, skipping\n",
    394                 TALER_B2S (&key->exchange_pub));
    395     return;
    396   }
    397   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    398               "Purging key %s\n",
    399               TALER_B2S (&key->exchange_pub));
    400   if (0 != unlink (key->filename))
    401     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    402                               "unlink",
    403                               key->filename);
    404   key->purge = true;
    405   key->key_gen = key_gen;
    406   GNUNET_free (key->filename);
    407 }
    408 
    409 
    410 /**
    411  * A @a client informs us that a key has been revoked.
    412  * Check if the key is still in use, and if so replace (!)
    413  * it with a fresh key.
    414  *
    415  * @param client the client making the request
    416  * @param rr the revocation request
    417  * @return #GNUNET_OK on success
    418  */
    419 static enum GNUNET_GenericReturnValue
    420 handle_revoke_request (struct TES_Client *client,
    421                        const struct TALER_CRYPTO_EddsaRevokeRequest *rr)
    422 {
    423   struct Key *key;
    424   struct Key *nkey;
    425 
    426   (void) client;
    427   key = NULL;
    428   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    429   for (struct Key *pos = keys_head;
    430        NULL != pos;
    431        pos = pos->next)
    432     if (0 == GNUNET_memcmp (&pos->exchange_pub,
    433                             &rr->exchange_pub))
    434     {
    435       key = pos;
    436       break;
    437     }
    438   if (NULL == key)
    439   {
    440     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    441     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    442                 "Revocation request ignored, key unknown\n");
    443     return GNUNET_OK;
    444   }
    445   if (key->purge)
    446   {
    447     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    448     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    449                 "Revocation request ignored, key %s already revoked\n",
    450                 TALER_B2S (&key->exchange_pub));
    451     return GNUNET_OK;
    452   }
    453   key_gen++;
    454   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    455               "Revoking key %s, bumping generation to %llu\n",
    456               TALER_B2S (&key->exchange_pub),
    457               (unsigned long long) key_gen);
    458   purge_key (key);
    459 
    460   /* Setup replacement key */
    461   nkey = GNUNET_new (struct Key);
    462   nkey->anchor = key->anchor;
    463   if (GNUNET_OK !=
    464       setup_key (nkey,
    465                  key))
    466   {
    467     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    468     GNUNET_break (0);
    469     GNUNET_SCHEDULER_shutdown ();
    470     globals->global_ret = EXIT_FAILURE;
    471     return GNUNET_SYSERR;
    472   }
    473   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    474   TES_wake_clients ();
    475   return GNUNET_OK;
    476 }
    477 
    478 
    479 /**
    480  * Handle @a hdr message received from @a client.
    481  *
    482  * @param client the client that received the message
    483  * @param hdr message that was received
    484  * @return #GNUNET_OK on success
    485  */
    486 static enum GNUNET_GenericReturnValue
    487 eddsa_work_dispatch (struct TES_Client *client,
    488                      const struct GNUNET_MessageHeader *hdr)
    489 {
    490   uint16_t msize = ntohs (hdr->size);
    491 
    492   switch (ntohs (hdr->type))
    493   {
    494   case TALER_HELPER_EDDSA_MT_REQ_SIGN:
    495     if (msize < sizeof (struct TALER_CRYPTO_EddsaSignRequest))
    496     {
    497       GNUNET_break_op (0);
    498       return GNUNET_SYSERR;
    499     }
    500     return handle_sign_request (
    501       client,
    502       (const struct TALER_CRYPTO_EddsaSignRequest *) hdr);
    503   case TALER_HELPER_EDDSA_MT_REQ_REVOKE:
    504     if (msize != sizeof (struct TALER_CRYPTO_EddsaRevokeRequest))
    505     {
    506       GNUNET_break_op (0);
    507       return GNUNET_SYSERR;
    508     }
    509     return handle_revoke_request (
    510       client,
    511       (const struct TALER_CRYPTO_EddsaRevokeRequest *) hdr);
    512   default:
    513     GNUNET_break_op (0);
    514     return GNUNET_SYSERR;
    515   }
    516 }
    517 
    518 
    519 /**
    520  * Send our initial key set to @a client together with the
    521  * "sync" terminator.
    522  *
    523  * @param client the client to inform
    524  * @return #GNUNET_OK on success
    525  */
    526 static enum GNUNET_GenericReturnValue
    527 eddsa_client_init (struct TES_Client *client)
    528 {
    529   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    530   for (struct Key *key = keys_head;
    531        NULL != key;
    532        key = key->next)
    533   {
    534     if (GNUNET_OK !=
    535         notify_client_key_add (client,
    536                                key))
    537     {
    538       GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    539       GNUNET_break (0);
    540       return GNUNET_SYSERR;
    541     }
    542   }
    543   client->key_gen = key_gen;
    544   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    545   {
    546     struct GNUNET_MessageHeader synced = {
    547       .type = htons (TALER_HELPER_EDDSA_SYNCED),
    548       .size = htons (sizeof (synced))
    549     };
    550 
    551     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    552                 "Client %p synced\n",
    553                 client);
    554     if (GNUNET_OK !=
    555         TES_transmit (client->csock,
    556                       &synced))
    557     {
    558       GNUNET_break (0);
    559       return GNUNET_SYSERR;
    560     }
    561   }
    562   return GNUNET_OK;
    563 }
    564 
    565 
    566 /**
    567  * Notify @a client about all changes to the keys since
    568  * the last generation known to the @a client.
    569  *
    570  * @param client the client to notify
    571  * @return #GNUNET_OK on success
    572  */
    573 static enum GNUNET_GenericReturnValue
    574 eddsa_update_client_keys (struct TES_Client *client)
    575 {
    576   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    577   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    578               "Updating client %p to generation %llu\n",
    579               client,
    580               (unsigned long long) key_gen);
    581   for (struct Key *key = keys_head;
    582        NULL != key;
    583        key = key->next)
    584   {
    585     if (key->key_gen <= client->key_gen)
    586     {
    587       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    588                   "Skipping key %s, no change since generation %llu\n",
    589                   TALER_B2S (&key->exchange_pub),
    590                   (unsigned long long) client->key_gen);
    591       continue;
    592     }
    593     if (key->purge)
    594     {
    595       if (GNUNET_OK !=
    596           notify_client_key_del (client,
    597                                  key))
    598       {
    599         GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    600         return GNUNET_SYSERR;
    601       }
    602     }
    603     else
    604     {
    605       if (GNUNET_OK !=
    606           notify_client_key_add (client,
    607                                  key))
    608       {
    609         GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    610         return GNUNET_SYSERR;
    611       }
    612     }
    613   }
    614   client->key_gen = key_gen;
    615   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    616   return GNUNET_OK;
    617 }
    618 
    619 
    620 /**
    621  * Create a new key (we do not have enough).
    622  *
    623  * @return #GNUNET_OK on success
    624  */
    625 static enum GNUNET_GenericReturnValue
    626 create_key (void)
    627 {
    628   struct Key *key;
    629   struct GNUNET_TIME_Timestamp anchor;
    630 
    631   anchor = GNUNET_TIME_timestamp_get ();
    632   if (NULL != keys_tail)
    633   {
    634     struct GNUNET_TIME_Absolute abs;
    635 
    636     abs = GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time,
    637                                     GNUNET_TIME_relative_subtract (
    638                                       duration,
    639                                       overlap_duration));
    640     if (GNUNET_TIME_absolute_cmp (anchor.abs_time,
    641                                   <,
    642                                   abs))
    643       anchor = GNUNET_TIME_absolute_to_timestamp (abs);
    644   }
    645   key = GNUNET_new (struct Key);
    646   key->anchor = anchor;
    647   if (GNUNET_OK !=
    648       setup_key (key,
    649                  keys_tail))
    650   {
    651     GNUNET_break (0);
    652     GNUNET_free (key);
    653     GNUNET_SCHEDULER_shutdown ();
    654     globals->global_ret = EXIT_FAILURE;
    655     return GNUNET_SYSERR;
    656   }
    657   return GNUNET_OK;
    658 }
    659 
    660 
    661 /**
    662  * At what time does the current key set require its next action?  Basically,
    663  * the minimum of the expiration time of the oldest key, and the expiration
    664  * time of the newest key minus the #lookahead_sign time.
    665  */
    666 static struct GNUNET_TIME_Absolute
    667 key_action_time (void)
    668 {
    669   struct Key *nxt;
    670 
    671   nxt = keys_head;
    672   while ( (NULL != nxt) &&
    673           (nxt->purge) )
    674     nxt = nxt->next;
    675   if (NULL == nxt)
    676     return GNUNET_TIME_UNIT_ZERO_ABS;
    677   return GNUNET_TIME_absolute_min (
    678     GNUNET_TIME_absolute_add (nxt->anchor.abs_time,
    679                               duration),
    680     GNUNET_TIME_absolute_subtract (
    681       GNUNET_TIME_absolute_subtract (
    682         GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time,
    683                                   duration),
    684         lookahead_sign),
    685       overlap_duration));
    686 }
    687 
    688 
    689 /**
    690  * Create new keys and expire ancient keys.
    691  *
    692  * @param cls NULL
    693  */
    694 static void
    695 update_keys (void *cls)
    696 {
    697   bool wake = false;
    698   struct Key *nxt;
    699 
    700   (void) cls;
    701   keygen_task = NULL;
    702   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    703   /* create new keys */
    704   while ( (NULL == keys_tail) ||
    705           GNUNET_TIME_absolute_is_past (
    706             GNUNET_TIME_absolute_subtract (
    707               GNUNET_TIME_absolute_subtract (
    708                 GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time,
    709                                           duration),
    710                 lookahead_sign),
    711               overlap_duration)) )
    712   {
    713     if (! wake)
    714     {
    715       key_gen++;
    716       wake = true;
    717     }
    718     if (GNUNET_OK !=
    719         create_key ())
    720     {
    721       GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    722       GNUNET_break (0);
    723       globals->global_ret = EXIT_FAILURE;
    724       GNUNET_SCHEDULER_shutdown ();
    725       return;
    726     }
    727   }
    728   nxt = keys_head;
    729   /* purge expired keys */
    730   while ( (NULL != nxt) &&
    731           GNUNET_TIME_absolute_is_past (
    732             GNUNET_TIME_absolute_add (nxt->anchor.abs_time,
    733                                       duration)))
    734   {
    735     if (! wake)
    736     {
    737       key_gen++;
    738       wake = true;
    739     }
    740     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    741                 "Purging past key %s (expired %s ago)\n",
    742                 TALER_B2S (&nxt->exchange_pub),
    743                 GNUNET_TIME_relative2s (
    744                   GNUNET_TIME_absolute_get_duration (
    745                     GNUNET_TIME_absolute_add (nxt->anchor.abs_time,
    746                                               duration)),
    747                   GNUNET_YES));
    748     purge_key (nxt);
    749     nxt = nxt->next;
    750   }
    751   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    752   if (wake)
    753     TES_wake_clients ();
    754   keygen_task = GNUNET_SCHEDULER_add_at (key_action_time (),
    755                                          &update_keys,
    756                                          NULL);
    757 }
    758 
    759 
    760 /**
    761  * Parse private key from @a filename in @a buf.
    762  *
    763  * @param filename name of the file we are parsing, for logging
    764  * @param buf key material
    765  * @param buf_size number of bytes in @a buf
    766  * @return #GNUNET_OK on success
    767  */
    768 static enum GNUNET_GenericReturnValue
    769 parse_key (const char *filename,
    770            const void *buf,
    771            size_t buf_size)
    772 {
    773   struct GNUNET_CRYPTO_EddsaPrivateKey priv;
    774   char *anchor_s;
    775   char dummy;
    776   unsigned long long anchor_ll;
    777   struct GNUNET_TIME_Timestamp anchor;
    778 
    779   anchor_s = strrchr (filename,
    780                       '/');
    781   if (NULL == anchor_s)
    782   {
    783     /* File in a directory without '/' in the name, this makes no sense. */
    784     GNUNET_break (0);
    785     return GNUNET_SYSERR;
    786   }
    787   anchor_s++;
    788   if (1 != sscanf (anchor_s,
    789                    "%llu%c",
    790                    &anchor_ll,
    791                    &dummy))
    792   {
    793     /* Filenames in KEYDIR must ONLY be the anchor time in seconds! */
    794     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    795                 "Filename `%s' invalid for key file, skipping\n",
    796                 filename);
    797     return GNUNET_SYSERR;
    798   }
    799   anchor.abs_time.abs_value_us = anchor_ll
    800                                  * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
    801   if (anchor_ll != anchor.abs_time.abs_value_us
    802       / GNUNET_TIME_UNIT_SECONDS.rel_value_us)
    803   {
    804     /* Integer overflow. Bad, invalid filename. */
    805     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    806                 "Filename `%s' invalid for key file, skipping\n",
    807                 filename);
    808     return GNUNET_SYSERR;
    809   }
    810   if (buf_size != sizeof (priv))
    811   {
    812     /* Parser failure. */
    813     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    814                 "File `%s' is malformed, skipping\n",
    815                 filename);
    816     return GNUNET_SYSERR;
    817   }
    818   GNUNET_memcpy (&priv,
    819                  buf,
    820                  buf_size);
    821 
    822   {
    823     struct GNUNET_CRYPTO_EddsaPublicKey pub;
    824     struct Key *key;
    825     struct Key *before;
    826 
    827     GNUNET_CRYPTO_eddsa_key_get_public (&priv,
    828                                         &pub);
    829     GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    830     key = GNUNET_new (struct Key);
    831     key->exchange_priv.eddsa_priv = priv;
    832     key->exchange_pub.eddsa_pub = pub;
    833     key->anchor = anchor;
    834     key->filename = GNUNET_strdup (filename);
    835     key->key_gen = key_gen;
    836     before = NULL;
    837     for (struct Key *pos = keys_head;
    838          NULL != pos;
    839          pos = pos->next)
    840     {
    841       if (GNUNET_TIME_timestamp_cmp (pos->anchor, >, anchor))
    842         break;
    843       before = pos;
    844     }
    845     GNUNET_CONTAINER_DLL_insert_after (keys_head,
    846                                        keys_tail,
    847                                        before,
    848                                        key);
    849     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    850     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    851                 "Imported key from `%s'\n",
    852                 filename);
    853   }
    854   return GNUNET_OK;
    855 }
    856 
    857 
    858 /**
    859  * Import a private key from @a filename.
    860  *
    861  * @param cls NULL
    862  * @param filename name of a file in the directory
    863  */
    864 static enum GNUNET_GenericReturnValue
    865 import_key (void *cls,
    866             const char *filename)
    867 {
    868   struct GNUNET_DISK_FileHandle *fh;
    869   struct GNUNET_DISK_MapHandle *map;
    870   void *ptr;
    871   int fd;
    872   struct stat sbuf;
    873 
    874   (void) cls;
    875   {
    876     struct stat lsbuf;
    877 
    878     if (0 != lstat (filename,
    879                     &lsbuf))
    880     {
    881       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    882                                 "lstat",
    883                                 filename);
    884       return GNUNET_OK;
    885     }
    886     if (! S_ISREG (lsbuf.st_mode))
    887     {
    888       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    889                   "File `%s' is not a regular file, which is not allowed for private keys!\n",
    890                   filename);
    891       return GNUNET_OK;
    892     }
    893   }
    894 
    895   fd = open (filename,
    896              O_RDONLY | O_CLOEXEC);
    897   if (-1 == fd)
    898   {
    899     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    900                               "open",
    901                               filename);
    902     return GNUNET_OK;
    903   }
    904   if (0 != fstat (fd,
    905                   &sbuf))
    906   {
    907     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    908                               "stat",
    909                               filename);
    910     GNUNET_break (0 == close (fd));
    911     return GNUNET_OK;
    912   }
    913   if (! S_ISREG (sbuf.st_mode))
    914   {
    915     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    916                 "File `%s' is not a regular file, which is not allowed for private keys!\n",
    917                 filename);
    918     GNUNET_break (0 == close (fd));
    919     return GNUNET_OK;
    920   }
    921   if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO)))
    922   {
    923     /* permission are NOT tight, try to patch them up! */
    924     if (0 !=
    925         fchmod (fd,
    926                 S_IRUSR))
    927     {
    928       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    929                                 "fchmod",
    930                                 filename);
    931       /* refuse to use key if file has wrong permissions */
    932       GNUNET_break (0 == close (fd));
    933       return GNUNET_OK;
    934     }
    935   }
    936   fh = GNUNET_DISK_get_handle_from_int_fd (fd);
    937   if (NULL == fh)
    938   {
    939     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    940                               "open",
    941                               filename);
    942     GNUNET_break (0 == close (fd));
    943     return GNUNET_OK;
    944   }
    945   if (sbuf.st_size > 2048)
    946   {
    947     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    948                 "File `%s' to big to be a private key\n",
    949                 filename);
    950     GNUNET_DISK_file_close (fh);
    951     return GNUNET_OK;
    952   }
    953   ptr = GNUNET_DISK_file_map (fh,
    954                               &map,
    955                               GNUNET_DISK_MAP_TYPE_READ,
    956                               (size_t) sbuf.st_size);
    957   if (NULL == ptr)
    958   {
    959     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    960                               "mmap",
    961                               filename);
    962     GNUNET_DISK_file_close (fh);
    963     return GNUNET_OK;
    964   }
    965   (void) parse_key (filename,
    966                     ptr,
    967                     (size_t) sbuf.st_size);
    968   GNUNET_DISK_file_unmap (map);
    969   GNUNET_DISK_file_close (fh);
    970   return GNUNET_OK;
    971 }
    972 
    973 
    974 /**
    975  * Load the various duration values from @a kcfg.
    976  *
    977  * @param cfg configuration to use
    978  * @return #GNUNET_OK on success
    979  */
    980 static enum GNUNET_GenericReturnValue
    981 load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg)
    982 {
    983   char *secname;
    984 
    985   GNUNET_asprintf (&secname,
    986                    "%s-secmod-eddsa",
    987                    globals->section);
    988   if (GNUNET_OK !=
    989       GNUNET_CONFIGURATION_get_value_time (cfg,
    990                                            secname,
    991                                            "OVERLAP_DURATION",
    992                                            &overlap_duration))
    993   {
    994     GNUNET_free (secname);
    995     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    996                                secname,
    997                                "OVERLAP_DURATION");
    998     return GNUNET_SYSERR;
    999   }
   1000   if (GNUNET_OK !=
   1001       GNUNET_CONFIGURATION_get_value_time (cfg,
   1002                                            secname,
   1003                                            "DURATION",
   1004                                            &duration))
   1005   {
   1006     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1007                                secname,
   1008                                "DURATION");
   1009     GNUNET_free (secname);
   1010     return GNUNET_SYSERR;
   1011   }
   1012   if (GNUNET_OK !=
   1013       GNUNET_CONFIGURATION_get_value_time (cfg,
   1014                                            secname,
   1015                                            "LOOKAHEAD_SIGN",
   1016                                            &lookahead_sign))
   1017   {
   1018     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1019                                secname,
   1020                                "LOOKAHEAD_SIGN");
   1021     GNUNET_free (secname);
   1022     return GNUNET_SYSERR;
   1023   }
   1024   GNUNET_free (secname);
   1025   return GNUNET_OK;
   1026 }
   1027 
   1028 
   1029 /**
   1030  * Function run on shutdown. Stops the various jobs (nicely).
   1031  *
   1032  * @param cls NULL
   1033  */
   1034 static void
   1035 do_shutdown (void *cls)
   1036 {
   1037   (void) cls;
   1038   TES_listen_stop ();
   1039   if (NULL != keygen_task)
   1040   {
   1041     GNUNET_SCHEDULER_cancel (keygen_task);
   1042     keygen_task = NULL;
   1043   }
   1044 }
   1045 
   1046 
   1047 void
   1048 TALER_SECMOD_eddsa_run (void *cls,
   1049                         char *const *args,
   1050                         const char *cfgfile,
   1051                         const struct GNUNET_CONFIGURATION_Handle *cfg)
   1052 {
   1053   static struct TES_Callbacks cb = {
   1054     .dispatch = eddsa_work_dispatch,
   1055     .updater = eddsa_update_client_keys,
   1056     .init = eddsa_client_init
   1057   };
   1058   struct TALER_SECMOD_Options *opt = cls;
   1059   char *secname;
   1060 
   1061   (void) args;
   1062   (void) cfgfile;
   1063   globals = opt;
   1064   if (GNUNET_TIME_timestamp_cmp (opt->global_now,
   1065                                  !=,
   1066                                  opt->global_now_tmp))
   1067   {
   1068     /* The user gave "--now", use it! */
   1069     opt->global_now = opt->global_now_tmp;
   1070   }
   1071   else
   1072   {
   1073     /* get current time again, we may be timetraveling! */
   1074     opt->global_now = GNUNET_TIME_timestamp_get ();
   1075   }
   1076   if (GNUNET_OK !=
   1077       load_durations (cfg))
   1078   {
   1079     opt->global_ret = EXIT_NOTCONFIGURED;
   1080     return;
   1081   }
   1082   GNUNET_asprintf (&secname,
   1083                    "%s-secmod-eddsa",
   1084                    opt->section);
   1085   if (GNUNET_OK !=
   1086       GNUNET_CONFIGURATION_get_value_filename (cfg,
   1087                                                secname,
   1088                                                "KEY_DIR",
   1089                                                &keydir))
   1090   {
   1091     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1092                                secname,
   1093                                "KEY_DIR");
   1094     GNUNET_free (secname);
   1095     opt->global_ret = EXIT_NOTCONFIGURED;
   1096     return;
   1097   }
   1098   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
   1099                                  NULL);
   1100   opt->global_ret = TES_listen_start (cfg,
   1101                                       secname,
   1102                                       &cb);
   1103   GNUNET_free (secname);
   1104   if (0 != opt->global_ret)
   1105     return;
   1106   /* Load keys */
   1107   GNUNET_break (GNUNET_OK ==
   1108                 GNUNET_DISK_directory_create (keydir));
   1109   GNUNET_DISK_directory_scan (keydir,
   1110                               &import_key,
   1111                               NULL);
   1112   if ( (NULL != keys_head) &&
   1113        (GNUNET_TIME_absolute_is_future (keys_head->anchor.abs_time)) )
   1114   {
   1115     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1116                 "Existing anchor is in %s the future. Refusing to start\n",
   1117                 GNUNET_TIME_relative2s (
   1118                   GNUNET_TIME_absolute_get_remaining (
   1119                     keys_head->anchor.abs_time),
   1120                   true));
   1121     opt->global_ret = EXIT_FAILURE;
   1122     GNUNET_SCHEDULER_shutdown ();
   1123     return;
   1124   }
   1125   /* start job to keep keys up-to-date; MUST be run before the #listen_task,
   1126      hence with priority. */
   1127   keygen_task = GNUNET_SCHEDULER_add_with_priority (
   1128     GNUNET_SCHEDULER_PRIORITY_URGENT,
   1129     &update_keys,
   1130     NULL);
   1131 }