LCOV - code coverage report
Current view: top level - util - secmod_eddsa.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 51.7 % 383 198
Test Date: 2026-01-04 22:17:00 Functions: 88.2 % 17 15

            Line data    Source code
       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          138 : notify_client_key_add (struct TES_Client *client,
     164              :                        const struct Key *key)
     165              : {
     166          276 :   struct TALER_CRYPTO_EddsaKeyAvailableNotification an = {
     167          138 :     .header.size = htons (sizeof (an)),
     168          138 :     .header.type = htons (TALER_HELPER_EDDSA_MT_AVAIL),
     169          138 :     .anchor_time = GNUNET_TIME_timestamp_hton (key->anchor),
     170          138 :     .duration = GNUNET_TIME_relative_hton (duration),
     171              :     .exchange_pub = key->exchange_pub,
     172              :     .secm_pub = TES_smpub
     173              :   };
     174              : 
     175          138 :   TALER_exchange_secmod_eddsa_sign (&key->exchange_pub,
     176              :                                     key->anchor,
     177              :                                     duration,
     178              :                                     &TES_smpriv,
     179              :                                     &an.secm_sig);
     180          138 :   if (GNUNET_OK !=
     181          138 :       TES_transmit (client->csock,
     182              :                     &an.header))
     183              :   {
     184            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     185              :                 "Client %p must have disconnected\n",
     186              :                 client);
     187            0 :     return GNUNET_SYSERR;
     188              :   }
     189          138 :   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            3 : notify_client_key_del (struct TES_Client *client,
     202              :                        const struct Key *key)
     203              : {
     204            3 :   struct TALER_CRYPTO_EddsaKeyPurgeNotification pn = {
     205            3 :     .header.type = htons (TALER_HELPER_EDDSA_MT_PURGE),
     206            3 :     .header.size = htons (sizeof (pn)),
     207              :     .exchange_pub = key->exchange_pub
     208              :   };
     209              : 
     210            3 :   if (GNUNET_OK !=
     211            3 :       TES_transmit (client->csock,
     212              :                     &pn.header))
     213              :   {
     214            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     215              :                 "Client %p must have disconnected\n",
     216              :                 client);
     217            0 :     return GNUNET_SYSERR;
     218              :   }
     219            3 :   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         1742 : handle_sign_request (struct TES_Client *client,
     234              :                      const struct TALER_CRYPTO_EddsaSignRequest *sr)
     235              : {
     236         1742 :   const struct GNUNET_CRYPTO_SignaturePurpose *purpose = &sr->purpose;
     237         1742 :   size_t purpose_size = ntohs (sr->header.size) - sizeof (*sr)
     238              :                         + sizeof (*purpose);
     239              :   struct Key *key;
     240         1742 :   struct TALER_CRYPTO_EddsaSignResponse sres = {
     241         1742 :     .header.size = htons (sizeof (sres)),
     242         1742 :     .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGNATURE)
     243              :   };
     244              :   enum TALER_ErrorCode ec;
     245              : 
     246         1742 :   if (purpose_size != htonl (purpose->size))
     247              :   {
     248            0 :     struct TALER_CRYPTO_EddsaSignFailure sf = {
     249            0 :       .header.size = htons (sizeof (sr)),
     250            0 :       .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE),
     251            0 :       .ec = htonl (TALER_EC_GENERIC_PARAMETER_MALFORMED)
     252              :     };
     253              : 
     254            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     255              :                 "Signing request failed, request malformed\n");
     256            0 :     return TES_transmit (client->csock,
     257              :                          &sf.header);
     258              :   }
     259              : 
     260         1742 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     261         1742 :   key = keys_head;
     262         3484 :   while ( (NULL != key) &&
     263         1742 :           (GNUNET_TIME_absolute_is_past (
     264              :              GNUNET_TIME_absolute_add (key->anchor.abs_time,
     265              :                                        duration))) )
     266              :   {
     267            0 :     struct Key *nxt = key->next;
     268              : 
     269            0 :     if (0 != key->rc)
     270            0 :       break; /* do later */
     271            0 :     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            0 :     GNUNET_CONTAINER_DLL_remove (keys_head,
     280              :                                  keys_tail,
     281              :                                  key);
     282            0 :     if ( (! key->purge) &&
     283            0 :          (0 != unlink (key->filename)) )
     284            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     285              :                                 "unlink",
     286              :                                 key->filename);
     287            0 :     GNUNET_free (key->filename);
     288            0 :     GNUNET_free (key);
     289            0 :     key = nxt;
     290              :   }
     291         1742 :   if (NULL == key)
     292              :   {
     293            0 :     GNUNET_break (0);
     294            0 :     ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
     295              :   }
     296              :   else
     297              :   {
     298         1742 :     GNUNET_assert (key->rc < UINT_MAX);
     299         1742 :     key->rc++;
     300         1742 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     301              : 
     302         1741 :     if (GNUNET_OK !=
     303         1742 :         GNUNET_CRYPTO_eddsa_sign_ (&key->exchange_priv.eddsa_priv,
     304              :                                    purpose,
     305              :                                    &sres.exchange_sig.eddsa_signature))
     306            0 :       ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     307              :     else
     308         1741 :       ec = TALER_EC_NONE;
     309         1741 :     sres.exchange_pub = key->exchange_pub;
     310         1741 :     GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     311         1742 :     GNUNET_assert (key->rc > 0);
     312         1742 :     key->rc--;
     313              :   }
     314         1742 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     315         1742 :   if (TALER_EC_NONE != ec)
     316              :   {
     317            0 :     struct TALER_CRYPTO_EddsaSignFailure sf = {
     318            0 :       .header.size = htons (sizeof (sf)),
     319            0 :       .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE),
     320            0 :       .ec = htonl ((uint32_t) ec)
     321              :     };
     322              : 
     323            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     324              :                 "Signing request %p failed, worker failed to produce signature\n",
     325              :                 client);
     326            0 :     return TES_transmit (client->csock,
     327              :                          &sf.header);
     328              :   }
     329         1742 :   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           66 : setup_key (struct Key *key,
     343              :            struct Key *position)
     344              : {
     345              :   struct GNUNET_CRYPTO_EddsaPrivateKey priv;
     346              :   struct GNUNET_CRYPTO_EddsaPublicKey pub;
     347              : 
     348           66 :   GNUNET_CRYPTO_eddsa_key_create (&priv);
     349           66 :   GNUNET_CRYPTO_eddsa_key_get_public (&priv,
     350              :                                       &pub);
     351           66 :   GNUNET_asprintf (&key->filename,
     352              :                    "%s/%llu",
     353              :                    keydir,
     354           66 :                    (unsigned long long) (key->anchor.abs_time.abs_value_us
     355           66 :                                          / GNUNET_TIME_UNIT_SECONDS.rel_value_us
     356              :                                          ));
     357           66 :   if (GNUNET_OK !=
     358           66 :       GNUNET_DISK_fn_write (key->filename,
     359              :                             &priv,
     360              :                             sizeof (priv),
     361              :                             GNUNET_DISK_PERM_USER_READ))
     362              :   {
     363            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     364              :                               "write",
     365              :                               key->filename);
     366            0 :     return GNUNET_SYSERR;
     367              :   }
     368           66 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     369              :               "Setup fresh private key in `%s'\n",
     370              :               key->filename);
     371           66 :   key->key_gen = key_gen;
     372           66 :   key->exchange_priv.eddsa_priv = priv;
     373           66 :   key->exchange_pub.eddsa_pub = pub;
     374           66 :   GNUNET_CONTAINER_DLL_insert_after (keys_head,
     375              :                                      keys_tail,
     376              :                                      position,
     377              :                                      key);
     378           66 :   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            3 : purge_key (struct Key *key)
     389              : {
     390            3 :   if (key->purge)
     391              :   {
     392            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     393              :                 "Key %s already purged, skipping\n",
     394              :                 TALER_B2S (&key->exchange_pub));
     395            0 :     return;
     396              :   }
     397            3 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     398              :               "Purging key %s\n",
     399              :               TALER_B2S (&key->exchange_pub));
     400            3 :   if (0 != unlink (key->filename))
     401            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     402              :                               "unlink",
     403              :                               key->filename);
     404            3 :   key->purge = true;
     405            3 :   key->key_gen = key_gen;
     406            3 :   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            3 : 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            3 :   key = NULL;
     428            3 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     429            3 :   for (struct Key *pos = keys_head;
     430           14 :        NULL != pos;
     431           11 :        pos = pos->next)
     432           14 :     if (0 == GNUNET_memcmp (&pos->exchange_pub,
     433              :                             &rr->exchange_pub))
     434              :     {
     435            3 :       key = pos;
     436            3 :       break;
     437              :     }
     438            3 :   if (NULL == key)
     439              :   {
     440            0 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     441            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     442              :                 "Revocation request ignored, key unknown\n");
     443            0 :     return GNUNET_OK;
     444              :   }
     445            3 :   if (key->purge)
     446              :   {
     447            0 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     448            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     449              :                 "Revocation request ignored, key %s already revoked\n",
     450              :                 TALER_B2S (&key->exchange_pub));
     451            0 :     return GNUNET_OK;
     452              :   }
     453            3 :   key_gen++;
     454            3 :   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            3 :   purge_key (key);
     459              : 
     460              :   /* Setup replacement key */
     461            3 :   nkey = GNUNET_new (struct Key);
     462            3 :   nkey->anchor = key->anchor;
     463            3 :   if (GNUNET_OK !=
     464            3 :       setup_key (nkey,
     465              :                  key))
     466              :   {
     467            0 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     468            0 :     GNUNET_break (0);
     469            0 :     GNUNET_SCHEDULER_shutdown ();
     470            0 :     globals->global_ret = EXIT_FAILURE;
     471            0 :     return GNUNET_SYSERR;
     472              :   }
     473            3 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     474            3 :   TES_wake_clients ();
     475            3 :   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         1745 : eddsa_work_dispatch (struct TES_Client *client,
     488              :                      const struct GNUNET_MessageHeader *hdr)
     489              : {
     490         1745 :   uint16_t msize = ntohs (hdr->size);
     491              : 
     492         1745 :   switch (ntohs (hdr->type))
     493              :   {
     494         1742 :   case TALER_HELPER_EDDSA_MT_REQ_SIGN:
     495         1742 :     if (msize < sizeof (struct TALER_CRYPTO_EddsaSignRequest))
     496              :     {
     497            0 :       GNUNET_break_op (0);
     498            0 :       return GNUNET_SYSERR;
     499              :     }
     500         1742 :     return handle_sign_request (
     501              :       client,
     502              :       (const struct TALER_CRYPTO_EddsaSignRequest *) hdr);
     503            3 :   case TALER_HELPER_EDDSA_MT_REQ_REVOKE:
     504            3 :     if (msize != sizeof (struct TALER_CRYPTO_EddsaRevokeRequest))
     505              :     {
     506            0 :       GNUNET_break_op (0);
     507            0 :       return GNUNET_SYSERR;
     508              :     }
     509            3 :     return handle_revoke_request (
     510              :       client,
     511              :       (const struct TALER_CRYPTO_EddsaRevokeRequest *) hdr);
     512            0 :   default:
     513            0 :     GNUNET_break_op (0);
     514            0 :     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           26 : eddsa_client_init (struct TES_Client *client)
     528              : {
     529           26 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     530           26 :   for (struct Key *key = keys_head;
     531          161 :        NULL != key;
     532          135 :        key = key->next)
     533              :   {
     534          135 :     if (GNUNET_OK !=
     535          135 :         notify_client_key_add (client,
     536              :                                key))
     537              :     {
     538            0 :       GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     539            0 :       GNUNET_break (0);
     540            0 :       return GNUNET_SYSERR;
     541              :     }
     542              :   }
     543           26 :   client->key_gen = key_gen;
     544           26 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     545              :   {
     546           26 :     struct GNUNET_MessageHeader synced = {
     547           26 :       .type = htons (TALER_HELPER_EDDSA_SYNCED),
     548           26 :       .size = htons (sizeof (synced))
     549              :     };
     550              : 
     551           26 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     552              :                 "Client %p synced\n",
     553              :                 client);
     554           26 :     if (GNUNET_OK !=
     555           26 :         TES_transmit (client->csock,
     556              :                       &synced))
     557              :     {
     558            0 :       GNUNET_break (0);
     559            0 :       return GNUNET_SYSERR;
     560              :     }
     561              :   }
     562           26 :   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           18 : eddsa_update_client_keys (struct TES_Client *client)
     575              : {
     576           18 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     577           18 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     578              :               "Updating client %p to generation %llu\n",
     579              :               client,
     580              :               (unsigned long long) key_gen);
     581           18 :   for (struct Key *key = keys_head;
     582           93 :        NULL != key;
     583           75 :        key = key->next)
     584              :   {
     585           75 :     if (key->key_gen <= client->key_gen)
     586              :     {
     587           69 :       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           69 :       continue;
     592              :     }
     593            6 :     if (key->purge)
     594              :     {
     595            3 :       if (GNUNET_OK !=
     596            3 :           notify_client_key_del (client,
     597              :                                  key))
     598              :       {
     599            0 :         GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     600            0 :         return GNUNET_SYSERR;
     601              :       }
     602              :     }
     603              :     else
     604              :     {
     605            3 :       if (GNUNET_OK !=
     606            3 :           notify_client_key_add (client,
     607              :                                  key))
     608              :       {
     609            0 :         GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     610            0 :         return GNUNET_SYSERR;
     611              :       }
     612              :     }
     613              :   }
     614           18 :   client->key_gen = key_gen;
     615           18 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     616           18 :   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           63 : create_key (void)
     627              : {
     628              :   struct Key *key;
     629              :   struct GNUNET_TIME_Timestamp anchor;
     630              : 
     631           63 :   anchor = GNUNET_TIME_timestamp_get ();
     632           63 :   if (NULL != keys_tail)
     633              :   {
     634              :     struct GNUNET_TIME_Absolute abs;
     635              : 
     636           45 :     abs = GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time,
     637              :                                     GNUNET_TIME_relative_subtract (
     638              :                                       duration,
     639              :                                       overlap_duration));
     640           45 :     if (GNUNET_TIME_absolute_cmp (anchor.abs_time,
     641              :                                   <,
     642              :                                   abs))
     643           45 :       anchor = GNUNET_TIME_absolute_to_timestamp (abs);
     644              :   }
     645           63 :   key = GNUNET_new (struct Key);
     646           63 :   key->anchor = anchor;
     647           63 :   if (GNUNET_OK !=
     648           63 :       setup_key (key,
     649              :                  keys_tail))
     650              :   {
     651            0 :     GNUNET_break (0);
     652            0 :     GNUNET_free (key);
     653            0 :     GNUNET_SCHEDULER_shutdown ();
     654            0 :     globals->global_ret = EXIT_FAILURE;
     655            0 :     return GNUNET_SYSERR;
     656              :   }
     657           63 :   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           18 : key_action_time (void)
     668              : {
     669              :   struct Key *nxt;
     670              : 
     671           18 :   nxt = keys_head;
     672           18 :   while ( (NULL != nxt) &&
     673           18 :           (nxt->purge) )
     674            0 :     nxt = nxt->next;
     675           18 :   if (NULL == nxt)
     676            0 :     return GNUNET_TIME_UNIT_ZERO_ABS;
     677           18 :   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           18 :         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           18 : update_keys (void *cls)
     696              : {
     697           18 :   bool wake = false;
     698              :   struct Key *nxt;
     699              : 
     700              :   (void) cls;
     701           18 :   keygen_task = NULL;
     702           18 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     703              :   /* create new keys */
     704          144 :   while ( (NULL == keys_tail) ||
     705           63 :           GNUNET_TIME_absolute_is_past (
     706              :             GNUNET_TIME_absolute_subtract (
     707              :               GNUNET_TIME_absolute_subtract (
     708           63 :                 GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time,
     709              :                                           duration),
     710              :                 lookahead_sign),
     711              :               overlap_duration)) )
     712              :   {
     713           63 :     if (! wake)
     714              :     {
     715           18 :       key_gen++;
     716           18 :       wake = true;
     717              :     }
     718           63 :     if (GNUNET_OK !=
     719           63 :         create_key ())
     720              :     {
     721            0 :       GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     722            0 :       GNUNET_break (0);
     723            0 :       globals->global_ret = EXIT_FAILURE;
     724            0 :       GNUNET_SCHEDULER_shutdown ();
     725            0 :       return;
     726              :     }
     727              :   }
     728           18 :   nxt = keys_head;
     729              :   /* purge expired keys */
     730           36 :   while ( (NULL != nxt) &&
     731           18 :           GNUNET_TIME_absolute_is_past (
     732              :             GNUNET_TIME_absolute_add (nxt->anchor.abs_time,
     733              :                                       duration)))
     734              :   {
     735            0 :     if (! wake)
     736              :     {
     737            0 :       key_gen++;
     738            0 :       wake = true;
     739              :     }
     740            0 :     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            0 :     purge_key (nxt);
     749            0 :     nxt = nxt->next;
     750              :   }
     751           18 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     752           18 :   if (wake)
     753           18 :     TES_wake_clients ();
     754           18 :   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            0 : 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            0 :   anchor_s = strrchr (filename,
     780              :                       '/');
     781            0 :   if (NULL == anchor_s)
     782              :   {
     783              :     /* File in a directory without '/' in the name, this makes no sense. */
     784            0 :     GNUNET_break (0);
     785            0 :     return GNUNET_SYSERR;
     786              :   }
     787            0 :   anchor_s++;
     788            0 :   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            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     795              :                 "Filename `%s' invalid for key file, skipping\n",
     796              :                 filename);
     797            0 :     return GNUNET_SYSERR;
     798              :   }
     799            0 :   anchor.abs_time.abs_value_us = anchor_ll
     800            0 :                                  * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
     801            0 :   if (anchor_ll != anchor.abs_time.abs_value_us
     802            0 :       / GNUNET_TIME_UNIT_SECONDS.rel_value_us)
     803              :   {
     804              :     /* Integer overflow. Bad, invalid filename. */
     805            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     806              :                 "Filename `%s' invalid for key file, skipping\n",
     807              :                 filename);
     808            0 :     return GNUNET_SYSERR;
     809              :   }
     810            0 :   if (buf_size != sizeof (priv))
     811              :   {
     812              :     /* Parser failure. */
     813            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     814              :                 "File `%s' is malformed, skipping\n",
     815              :                 filename);
     816            0 :     return GNUNET_SYSERR;
     817              :   }
     818            0 :   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            0 :     GNUNET_CRYPTO_eddsa_key_get_public (&priv,
     828              :                                         &pub);
     829            0 :     GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     830            0 :     key = GNUNET_new (struct Key);
     831            0 :     key->exchange_priv.eddsa_priv = priv;
     832            0 :     key->exchange_pub.eddsa_pub = pub;
     833            0 :     key->anchor = anchor;
     834            0 :     key->filename = GNUNET_strdup (filename);
     835            0 :     key->key_gen = key_gen;
     836            0 :     before = NULL;
     837            0 :     for (struct Key *pos = keys_head;
     838            0 :          NULL != pos;
     839            0 :          pos = pos->next)
     840              :     {
     841            0 :       if (GNUNET_TIME_timestamp_cmp (pos->anchor, >, anchor))
     842            0 :         break;
     843            0 :       before = pos;
     844              :     }
     845            0 :     GNUNET_CONTAINER_DLL_insert_after (keys_head,
     846              :                                        keys_tail,
     847              :                                        before,
     848              :                                        key);
     849            0 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     850            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     851              :                 "Imported key from `%s'\n",
     852              :                 filename);
     853              :   }
     854            0 :   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            0 : 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            0 :     if (0 != lstat (filename,
     879              :                     &lsbuf))
     880              :     {
     881            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     882              :                                 "lstat",
     883              :                                 filename);
     884            0 :       return GNUNET_OK;
     885              :     }
     886            0 :     if (! S_ISREG (lsbuf.st_mode))
     887              :     {
     888            0 :       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            0 :       return GNUNET_OK;
     892              :     }
     893              :   }
     894              : 
     895            0 :   fd = open (filename,
     896              :              O_RDONLY | O_CLOEXEC);
     897            0 :   if (-1 == fd)
     898              :   {
     899            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     900              :                               "open",
     901              :                               filename);
     902            0 :     return GNUNET_OK;
     903              :   }
     904            0 :   if (0 != fstat (fd,
     905              :                   &sbuf))
     906              :   {
     907            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     908              :                               "stat",
     909              :                               filename);
     910            0 :     GNUNET_break (0 == close (fd));
     911            0 :     return GNUNET_OK;
     912              :   }
     913            0 :   if (! S_ISREG (sbuf.st_mode))
     914              :   {
     915            0 :     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            0 :     GNUNET_break (0 == close (fd));
     919            0 :     return GNUNET_OK;
     920              :   }
     921            0 :   if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO)))
     922              :   {
     923              :     /* permission are NOT tight, try to patch them up! */
     924            0 :     if (0 !=
     925            0 :         fchmod (fd,
     926              :                 S_IRUSR))
     927              :     {
     928            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     929              :                                 "fchmod",
     930              :                                 filename);
     931              :       /* refuse to use key if file has wrong permissions */
     932            0 :       GNUNET_break (0 == close (fd));
     933            0 :       return GNUNET_OK;
     934              :     }
     935              :   }
     936            0 :   fh = GNUNET_DISK_get_handle_from_int_fd (fd);
     937            0 :   if (NULL == fh)
     938              :   {
     939            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     940              :                               "open",
     941              :                               filename);
     942            0 :     GNUNET_break (0 == close (fd));
     943            0 :     return GNUNET_OK;
     944              :   }
     945            0 :   if (sbuf.st_size > 2048)
     946              :   {
     947            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     948              :                 "File `%s' to big to be a private key\n",
     949              :                 filename);
     950            0 :     GNUNET_DISK_file_close (fh);
     951            0 :     return GNUNET_OK;
     952              :   }
     953            0 :   ptr = GNUNET_DISK_file_map (fh,
     954              :                               &map,
     955              :                               GNUNET_DISK_MAP_TYPE_READ,
     956            0 :                               (size_t) sbuf.st_size);
     957            0 :   if (NULL == ptr)
     958              :   {
     959            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     960              :                               "mmap",
     961              :                               filename);
     962            0 :     GNUNET_DISK_file_close (fh);
     963            0 :     return GNUNET_OK;
     964              :   }
     965            0 :   (void) parse_key (filename,
     966              :                     ptr,
     967            0 :                     (size_t) sbuf.st_size);
     968            0 :   GNUNET_DISK_file_unmap (map);
     969            0 :   GNUNET_DISK_file_close (fh);
     970            0 :   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           18 : load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg)
     982              : {
     983              :   char *secname;
     984              : 
     985           18 :   GNUNET_asprintf (&secname,
     986              :                    "%s-secmod-eddsa",
     987           18 :                    globals->section);
     988           18 :   if (GNUNET_OK !=
     989           18 :       GNUNET_CONFIGURATION_get_value_time (cfg,
     990              :                                            secname,
     991              :                                            "OVERLAP_DURATION",
     992              :                                            &overlap_duration))
     993              :   {
     994            0 :     GNUNET_free (secname);
     995            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     996              :                                secname,
     997              :                                "OVERLAP_DURATION");
     998            0 :     return GNUNET_SYSERR;
     999              :   }
    1000           18 :   if (GNUNET_OK !=
    1001           18 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    1002              :                                            secname,
    1003              :                                            "DURATION",
    1004              :                                            &duration))
    1005              :   {
    1006            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1007              :                                secname,
    1008              :                                "DURATION");
    1009            0 :     GNUNET_free (secname);
    1010            0 :     return GNUNET_SYSERR;
    1011              :   }
    1012           18 :   if (GNUNET_OK !=
    1013           18 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    1014              :                                            secname,
    1015              :                                            "LOOKAHEAD_SIGN",
    1016              :                                            &lookahead_sign))
    1017              :   {
    1018            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1019              :                                secname,
    1020              :                                "LOOKAHEAD_SIGN");
    1021            0 :     GNUNET_free (secname);
    1022            0 :     return GNUNET_SYSERR;
    1023              :   }
    1024           18 :   GNUNET_free (secname);
    1025           18 :   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           18 : do_shutdown (void *cls)
    1036              : {
    1037              :   (void) cls;
    1038           18 :   TES_listen_stop ();
    1039           18 :   if (NULL != keygen_task)
    1040              :   {
    1041           18 :     GNUNET_SCHEDULER_cancel (keygen_task);
    1042           18 :     keygen_task = NULL;
    1043              :   }
    1044           18 : }
    1045              : 
    1046              : 
    1047              : void
    1048           18 : 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           18 :   struct TALER_SECMOD_Options *opt = cls;
    1059              :   char *secname;
    1060              : 
    1061              :   (void) args;
    1062              :   (void) cfgfile;
    1063           18 :   globals = opt;
    1064           18 :   if (GNUNET_TIME_timestamp_cmp (opt->global_now,
    1065              :                                  !=,
    1066              :                                  opt->global_now_tmp))
    1067              :   {
    1068              :     /* The user gave "--now", use it! */
    1069            0 :     opt->global_now = opt->global_now_tmp;
    1070              :   }
    1071              :   else
    1072              :   {
    1073              :     /* get current time again, we may be timetraveling! */
    1074           18 :     opt->global_now = GNUNET_TIME_timestamp_get ();
    1075              :   }
    1076           18 :   if (GNUNET_OK !=
    1077           18 :       load_durations (cfg))
    1078              :   {
    1079            0 :     opt->global_ret = EXIT_NOTCONFIGURED;
    1080            0 :     return;
    1081              :   }
    1082           18 :   GNUNET_asprintf (&secname,
    1083              :                    "%s-secmod-eddsa",
    1084              :                    opt->section);
    1085           18 :   if (GNUNET_OK !=
    1086           18 :       GNUNET_CONFIGURATION_get_value_filename (cfg,
    1087              :                                                secname,
    1088              :                                                "KEY_DIR",
    1089              :                                                &keydir))
    1090              :   {
    1091            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1092              :                                secname,
    1093              :                                "KEY_DIR");
    1094            0 :     GNUNET_free (secname);
    1095            0 :     opt->global_ret = EXIT_NOTCONFIGURED;
    1096            0 :     return;
    1097              :   }
    1098           18 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    1099              :                                  NULL);
    1100           18 :   opt->global_ret = TES_listen_start (cfg,
    1101              :                                       secname,
    1102              :                                       &cb);
    1103           18 :   GNUNET_free (secname);
    1104           18 :   if (0 != opt->global_ret)
    1105            0 :     return;
    1106              :   /* Load keys */
    1107           18 :   GNUNET_break (GNUNET_OK ==
    1108              :                 GNUNET_DISK_directory_create (keydir));
    1109           18 :   GNUNET_DISK_directory_scan (keydir,
    1110              :                               &import_key,
    1111              :                               NULL);
    1112           18 :   if ( (NULL != keys_head) &&
    1113            0 :        (GNUNET_TIME_absolute_is_future (keys_head->anchor.abs_time)) )
    1114              :   {
    1115            0 :     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            0 :     opt->global_ret = EXIT_FAILURE;
    1122            0 :     GNUNET_SCHEDULER_shutdown ();
    1123            0 :     return;
    1124              :   }
    1125              :   /* start job to keep keys up-to-date; MUST be run before the #listen_task,
    1126              :      hence with priority. */
    1127           18 :   keygen_task = GNUNET_SCHEDULER_add_with_priority (
    1128              :     GNUNET_SCHEDULER_PRIORITY_URGENT,
    1129              :     &update_keys,
    1130              :     NULL);
    1131              : }
        

Generated by: LCOV version 2.0-1