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

Generated by: LCOV version 2.0-1