LCOV - code coverage report
Current view: top level - util - secmod_eddsa.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 52.2 % 387 202
Test Date: 2026-09-11 18:55:36 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          102 : notify_client_key_add (struct TES_Client *client,
     164              :                        const struct Key *key)
     165              : {
     166          204 :   struct TALER_CRYPTO_EddsaKeyAvailableNotification an = {
     167          102 :     .header.size = htons (sizeof (an)),
     168          102 :     .header.type = htons (TALER_HELPER_EDDSA_MT_AVAIL),
     169          102 :     .anchor_time = GNUNET_TIME_timestamp_hton (key->anchor),
     170          102 :     .duration = GNUNET_TIME_relative_hton (duration),
     171              :     .exchange_pub = key->exchange_pub,
     172              :     .secm_pub = TES_smpub
     173              :   };
     174              : 
     175          102 :   TALER_exchange_secmod_eddsa_sign (&key->exchange_pub,
     176              :                                     key->anchor,
     177              :                                     duration,
     178              :                                     &TES_smpriv,
     179              :                                     &an.secm_sig);
     180          102 :   if (GNUNET_OK !=
     181          102 :       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          102 :   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         1148 : handle_sign_request (struct TES_Client *client,
     234              :                      const struct TALER_CRYPTO_EddsaSignRequest *sr)
     235              : {
     236         1148 :   const struct GNUNET_CRYPTO_SignaturePurpose *purpose = &sr->purpose;
     237         1148 :   size_t purpose_size = ntohs (sr->header.size) - sizeof (*sr)
     238              :                         + sizeof (*purpose);
     239              :   struct Key *key;
     240         1148 :   struct TALER_CRYPTO_EddsaSignResponse sres = {
     241         1148 :     .header.size = htons (sizeof (sres)),
     242         1148 :     .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGNATURE)
     243              :   };
     244              :   enum TALER_ErrorCode ec;
     245              : 
     246         1148 :   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         1148 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     261         1148 :   key = keys_head;
     262         2296 :   while ( (NULL != key) &&
     263         1148 :           (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         2051 :   while ( (NULL != key) &&
     294         2051 :           (key->purge) )
     295          903 :     key = key->next;
     296         1148 :   if (NULL == key)
     297              :   {
     298            0 :     GNUNET_break (0);
     299            0 :     ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
     300              :   }
     301              :   else
     302              :   {
     303         1148 :     GNUNET_assert (key->rc < UINT_MAX);
     304         1148 :     key->rc++;
     305         1148 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     306              : 
     307         1147 :     if (GNUNET_OK !=
     308         1148 :         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         1147 :       ec = TALER_EC_NONE;
     314         1147 :     sres.exchange_pub = key->exchange_pub;
     315         1147 :     GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     316         1148 :     GNUNET_assert (key->rc > 0);
     317         1148 :     key->rc--;
     318              :   }
     319         1148 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     320         1146 :   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         1146 :   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           30 : setup_key (struct Key *key,
     348              :            struct Key *position)
     349              : {
     350              :   struct GNUNET_CRYPTO_EddsaPrivateKey priv;
     351              :   struct GNUNET_CRYPTO_EddsaPublicKey pub;
     352              : 
     353           30 :   GNUNET_CRYPTO_eddsa_key_create (&priv);
     354           30 :   GNUNET_CRYPTO_eddsa_key_get_public (&priv,
     355              :                                       &pub);
     356           30 :   GNUNET_asprintf (&key->filename,
     357              :                    "%s/%llu",
     358              :                    keydir,
     359           30 :                    (unsigned long long) (key->anchor.abs_time.abs_value_us
     360           30 :                                          / GNUNET_TIME_UNIT_SECONDS.rel_value_us
     361              :                                          ));
     362           30 :   if (GNUNET_OK !=
     363           30 :       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           30 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     374              :               "Setup fresh private key in `%s'\n",
     375              :               key->filename);
     376           30 :   key->key_gen = key_gen;
     377           30 :   key->exchange_priv.eddsa_priv = priv;
     378           30 :   key->exchange_pub.eddsa_pub = pub;
     379           30 :   GNUNET_CONTAINER_DLL_insert_after (keys_head,
     380              :                                      keys_tail,
     381              :                                      position,
     382              :                                      key);
     383           30 :   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            3 : purge_key (struct Key *key)
     394              : {
     395            3 :   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            3 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     403              :               "Purging key %s\n",
     404              :               TALER_B2S (&key->exchange_pub));
     405            3 :   if (0 != unlink (key->filename))
     406            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     407              :                               "unlink",
     408              :                               key->filename);
     409            3 :   key->purge = true;
     410            3 :   key->key_gen = key_gen;
     411            3 :   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            3 : 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            3 :   key = NULL;
     433            3 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     434            3 :   for (struct Key *pos = keys_head;
     435           11 :        NULL != pos;
     436            8 :        pos = pos->next)
     437           11 :     if (0 == GNUNET_memcmp (&pos->exchange_pub,
     438              :                             &rr->exchange_pub))
     439              :     {
     440            3 :       key = pos;
     441            3 :       break;
     442              :     }
     443            3 :   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            3 :   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            3 :   key_gen++;
     459            3 :   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            3 :   purge_key (key);
     464              : 
     465              :   /* Setup replacement key */
     466            3 :   nkey = GNUNET_new (struct Key);
     467            3 :   nkey->anchor = key->anchor;
     468            3 :   if (GNUNET_OK !=
     469            3 :       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            3 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     479            3 :   TES_wake_clients ();
     480            3 :   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         1151 : eddsa_work_dispatch (struct TES_Client *client,
     493              :                      const struct GNUNET_MessageHeader *hdr)
     494              : {
     495         1151 :   uint16_t msize = ntohs (hdr->size);
     496              : 
     497         1151 :   switch (ntohs (hdr->type))
     498              :   {
     499         1148 :   case TALER_HELPER_EDDSA_MT_REQ_SIGN:
     500         1148 :     if (msize < sizeof (struct TALER_CRYPTO_EddsaSignRequest))
     501              :     {
     502            0 :       GNUNET_break_op (0);
     503            0 :       return GNUNET_SYSERR;
     504              :     }
     505         1148 :     return handle_sign_request (
     506              :       client,
     507              :       (const struct TALER_CRYPTO_EddsaSignRequest *) hdr);
     508            3 :   case TALER_HELPER_EDDSA_MT_REQ_REVOKE:
     509            3 :     if (msize != sizeof (struct TALER_CRYPTO_EddsaRevokeRequest))
     510              :     {
     511            0 :       GNUNET_break_op (0);
     512            0 :       return GNUNET_SYSERR;
     513              :     }
     514            3 :     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           26 : eddsa_client_init (struct TES_Client *client)
     533              : {
     534           26 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     535           26 :   for (struct Key *key = keys_head;
     536          125 :        NULL != key;
     537           99 :        key = key->next)
     538              :   {
     539           99 :     if (GNUNET_OK !=
     540           99 :         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           26 :   client->key_gen = key_gen;
     549           26 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     550              :   {
     551           26 :     struct GNUNET_MessageHeader synced = {
     552           26 :       .type = htons (TALER_HELPER_EDDSA_SYNCED),
     553           26 :       .size = htons (sizeof (synced))
     554              :     };
     555              : 
     556           26 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     557              :                 "Client %p synced\n",
     558              :                 client);
     559           26 :     if (GNUNET_OK !=
     560           26 :         TES_transmit (client->csock,
     561              :                       &synced))
     562              :     {
     563            0 :       GNUNET_break (0);
     564            0 :       return GNUNET_SYSERR;
     565              :     }
     566              :   }
     567           26 :   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           20 : eddsa_update_client_keys (struct TES_Client *client)
     580              : {
     581           20 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     582           20 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     583              :               "Updating client %p to generation %llu\n",
     584              :               client,
     585              :               (unsigned long long) key_gen);
     586           20 :   for (struct Key *key = keys_head;
     587           65 :        NULL != key;
     588           45 :        key = key->next)
     589              :   {
     590           45 :     if (key->key_gen <= client->key_gen)
     591              :     {
     592           39 :       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           39 :       continue;
     597              :     }
     598            6 :     if (key->purge)
     599              :     {
     600            3 :       if (GNUNET_OK !=
     601            3 :           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            3 :       if (GNUNET_OK !=
     611            3 :           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           20 :   client->key_gen = key_gen;
     620           20 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     621           20 :   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           27 : create_key (void)
     632              : {
     633              :   struct Key *key;
     634              :   struct GNUNET_TIME_Timestamp anchor;
     635              : 
     636           27 :   anchor = GNUNET_TIME_timestamp_get ();
     637           27 :   if (NULL != keys_tail)
     638              :   {
     639              :     struct GNUNET_TIME_Absolute abs;
     640              : 
     641            9 :     abs = GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time,
     642              :                                     GNUNET_TIME_relative_subtract (
     643              :                                       duration,
     644              :                                       overlap_duration));
     645            9 :     if (GNUNET_TIME_absolute_cmp (anchor.abs_time,
     646              :                                   <,
     647              :                                   abs))
     648            9 :       anchor = GNUNET_TIME_absolute_to_timestamp (abs);
     649              :   }
     650           27 :   key = GNUNET_new (struct Key);
     651           27 :   key->anchor = anchor;
     652           27 :   if (GNUNET_OK !=
     653           27 :       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           27 :   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           18 : key_action_time (void)
     675              : {
     676              :   struct Key *nxt;
     677              : 
     678           18 :   nxt = keys_head;
     679           18 :   while ( (NULL != nxt) &&
     680           18 :           (nxt->purge) )
     681            0 :     nxt = nxt->next;
     682           18 :   if (NULL == nxt)
     683            0 :     return GNUNET_TIME_UNIT_ZERO_ABS;
     684           18 :   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           18 :         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           18 : update_keys (void *cls)
     703              : {
     704           18 :   bool wake = false;
     705              :   struct Key *nxt;
     706              :   struct GNUNET_TIME_Absolute at;
     707              : 
     708              :   (void) cls;
     709           18 :   keygen_task = NULL;
     710           18 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     711              :   /* create new keys */
     712           72 :   while ( (NULL == keys_tail) ||
     713           27 :           GNUNET_TIME_absolute_is_past (
     714              :             GNUNET_TIME_absolute_subtract (
     715              :               GNUNET_TIME_absolute_subtract (
     716           27 :                 GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time,
     717              :                                           duration),
     718              :                 lookahead_sign),
     719              :               overlap_duration)) )
     720              :   {
     721           27 :     if (! wake)
     722              :     {
     723           18 :       key_gen++;
     724           18 :       wake = true;
     725              :     }
     726           27 :     if (GNUNET_OK !=
     727           27 :         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           18 :   nxt = keys_head;
     737              :   /* purge expired keys */
     738           36 :   while ( (NULL != nxt) &&
     739           18 :           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           18 :   at = key_action_time ();
     760           18 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     761           18 :   if (wake)
     762           18 :     TES_wake_clients ();
     763           18 :   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              :   {
     885              :     struct stat lsbuf;
     886              : 
     887            0 :     if (0 != lstat (filename,
     888              :                     &lsbuf))
     889              :     {
     890            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     891              :                                 "lstat",
     892              :                                 filename);
     893            0 :       return GNUNET_OK;
     894              :     }
     895            0 :     if (! S_ISREG (lsbuf.st_mode))
     896              :     {
     897            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     898              :                   "File `%s' is not a regular file, which is not allowed for private keys!\n",
     899              :                   filename);
     900            0 :       return GNUNET_OK;
     901              :     }
     902              :   }
     903              : 
     904            0 :   fd = open (filename,
     905              :              O_RDONLY | O_CLOEXEC);
     906            0 :   if (-1 == fd)
     907              :   {
     908            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     909              :                               "open",
     910              :                               filename);
     911            0 :     return GNUNET_OK;
     912              :   }
     913            0 :   if (0 != fstat (fd,
     914              :                   &sbuf))
     915              :   {
     916            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     917              :                               "stat",
     918              :                               filename);
     919            0 :     GNUNET_break (0 == close (fd));
     920            0 :     return GNUNET_OK;
     921              :   }
     922            0 :   if (! S_ISREG (sbuf.st_mode))
     923              :   {
     924            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     925              :                 "File `%s' is not a regular file, which is not allowed for private keys!\n",
     926              :                 filename);
     927            0 :     GNUNET_break (0 == close (fd));
     928            0 :     return GNUNET_OK;
     929              :   }
     930            0 :   if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO)))
     931              :   {
     932              :     /* permission are NOT tight, try to patch them up! */
     933            0 :     if (0 !=
     934            0 :         fchmod (fd,
     935              :                 S_IRUSR))
     936              :     {
     937            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     938              :                                 "fchmod",
     939              :                                 filename);
     940              :       /* refuse to use key if file has wrong permissions */
     941            0 :       GNUNET_break (0 == close (fd));
     942            0 :       return GNUNET_OK;
     943              :     }
     944              :   }
     945            0 :   fh = GNUNET_DISK_get_handle_from_int_fd (fd);
     946            0 :   if (NULL == fh)
     947              :   {
     948            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     949              :                               "open",
     950              :                               filename);
     951            0 :     GNUNET_break (0 == close (fd));
     952            0 :     return GNUNET_OK;
     953              :   }
     954            0 :   if (sbuf.st_size > 2048)
     955              :   {
     956            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     957              :                 "File `%s' to big to be a private key\n",
     958              :                 filename);
     959            0 :     GNUNET_DISK_file_close (fh);
     960            0 :     return GNUNET_OK;
     961              :   }
     962            0 :   ptr = GNUNET_DISK_file_map (fh,
     963              :                               &map,
     964              :                               GNUNET_DISK_MAP_TYPE_READ,
     965            0 :                               (size_t) sbuf.st_size);
     966            0 :   if (NULL == ptr)
     967              :   {
     968            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     969              :                               "mmap",
     970              :                               filename);
     971            0 :     GNUNET_DISK_file_close (fh);
     972            0 :     return GNUNET_OK;
     973              :   }
     974            0 :   (void) parse_key (filename,
     975              :                     ptr,
     976            0 :                     (size_t) sbuf.st_size);
     977            0 :   GNUNET_DISK_file_unmap (map);
     978            0 :   GNUNET_DISK_file_close (fh);
     979            0 :   return GNUNET_OK;
     980              : }
     981              : 
     982              : 
     983              : /**
     984              :  * Load the various duration values from @a kcfg.
     985              :  *
     986              :  * @param cfg configuration to use
     987              :  * @return #GNUNET_OK on success
     988              :  */
     989              : static enum GNUNET_GenericReturnValue
     990           18 : load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg)
     991              : {
     992              :   char *secname;
     993              : 
     994           18 :   GNUNET_asprintf (&secname,
     995              :                    "%s-secmod-eddsa",
     996           18 :                    globals->section);
     997           18 :   if (GNUNET_OK !=
     998           18 :       GNUNET_CONFIGURATION_get_value_time (cfg,
     999              :                                            secname,
    1000              :                                            "OVERLAP_DURATION",
    1001              :                                            &overlap_duration))
    1002              :   {
    1003            0 :     GNUNET_free (secname);
    1004            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1005              :                                secname,
    1006              :                                "OVERLAP_DURATION");
    1007            0 :     return GNUNET_SYSERR;
    1008              :   }
    1009           18 :   if (GNUNET_OK !=
    1010           18 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    1011              :                                            secname,
    1012              :                                            "DURATION",
    1013              :                                            &duration))
    1014              :   {
    1015            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1016              :                                secname,
    1017              :                                "DURATION");
    1018            0 :     GNUNET_free (secname);
    1019            0 :     return GNUNET_SYSERR;
    1020              :   }
    1021           18 :   if (GNUNET_OK !=
    1022           18 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    1023              :                                            secname,
    1024              :                                            "LOOKAHEAD_SIGN",
    1025              :                                            &lookahead_sign))
    1026              :   {
    1027            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1028              :                                secname,
    1029              :                                "LOOKAHEAD_SIGN");
    1030            0 :     GNUNET_free (secname);
    1031            0 :     return GNUNET_SYSERR;
    1032              :   }
    1033           18 :   GNUNET_free (secname);
    1034           18 :   return GNUNET_OK;
    1035              : }
    1036              : 
    1037              : 
    1038              : /**
    1039              :  * Function run on shutdown. Stops the various jobs (nicely).
    1040              :  *
    1041              :  * @param cls NULL
    1042              :  */
    1043              : static void
    1044           18 : do_shutdown (void *cls)
    1045              : {
    1046              :   (void) cls;
    1047           18 :   TES_listen_stop ();
    1048           18 :   if (NULL != keygen_task)
    1049              :   {
    1050           18 :     GNUNET_SCHEDULER_cancel (keygen_task);
    1051           18 :     keygen_task = NULL;
    1052              :   }
    1053           18 : }
    1054              : 
    1055              : 
    1056              : void
    1057           18 : TALER_SECMOD_eddsa_run (void *cls,
    1058              :                         char *const *args,
    1059              :                         const char *cfgfile,
    1060              :                         const struct GNUNET_CONFIGURATION_Handle *cfg)
    1061              : {
    1062              :   static struct TES_Callbacks cb = {
    1063              :     .dispatch = eddsa_work_dispatch,
    1064              :     .updater = eddsa_update_client_keys,
    1065              :     .init = eddsa_client_init
    1066              :   };
    1067           18 :   struct TALER_SECMOD_Options *opt = cls;
    1068              :   char *secname;
    1069              : 
    1070              :   (void) args;
    1071              :   (void) cfgfile;
    1072           18 :   globals = opt;
    1073           18 :   if (GNUNET_TIME_timestamp_cmp (opt->global_now,
    1074              :                                  !=,
    1075              :                                  opt->global_now_tmp))
    1076              :   {
    1077              :     /* The user gave "--now", use it! */
    1078            0 :     opt->global_now = opt->global_now_tmp;
    1079              :   }
    1080              :   else
    1081              :   {
    1082              :     /* get current time again, we may be timetraveling! */
    1083           18 :     opt->global_now = GNUNET_TIME_timestamp_get ();
    1084              :   }
    1085           18 :   if (GNUNET_OK !=
    1086           18 :       load_durations (cfg))
    1087              :   {
    1088            0 :     opt->global_ret = EXIT_NOTCONFIGURED;
    1089            0 :     return;
    1090              :   }
    1091           18 :   GNUNET_asprintf (&secname,
    1092              :                    "%s-secmod-eddsa",
    1093              :                    opt->section);
    1094           18 :   if (GNUNET_OK !=
    1095           18 :       GNUNET_CONFIGURATION_get_value_filename (cfg,
    1096              :                                                secname,
    1097              :                                                "KEY_DIR",
    1098              :                                                &keydir))
    1099              :   {
    1100            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1101              :                                secname,
    1102              :                                "KEY_DIR");
    1103            0 :     GNUNET_free (secname);
    1104            0 :     opt->global_ret = EXIT_NOTCONFIGURED;
    1105            0 :     return;
    1106              :   }
    1107           18 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    1108              :                                  NULL);
    1109           18 :   opt->global_ret = TES_listen_start (cfg,
    1110              :                                       secname,
    1111              :                                       &cb);
    1112           18 :   GNUNET_free (secname);
    1113           18 :   if (0 != opt->global_ret)
    1114            0 :     return;
    1115              :   /* Load keys */
    1116           18 :   GNUNET_break (GNUNET_OK ==
    1117              :                 GNUNET_DISK_directory_create (keydir));
    1118           18 :   GNUNET_DISK_directory_scan (keydir,
    1119              :                               &import_key,
    1120              :                               NULL);
    1121           18 :   if ( (NULL != keys_head) &&
    1122            0 :        (GNUNET_TIME_absolute_is_future (keys_head->anchor.abs_time)) )
    1123              :   {
    1124            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1125              :                 "Existing anchor is in %s the future. Refusing to start\n",
    1126              :                 GNUNET_TIME_relative2s (
    1127              :                   GNUNET_TIME_absolute_get_remaining (
    1128              :                     keys_head->anchor.abs_time),
    1129              :                   true));
    1130            0 :     opt->global_ret = EXIT_FAILURE;
    1131            0 :     GNUNET_SCHEDULER_shutdown ();
    1132            0 :     return;
    1133              :   }
    1134              :   /* start job to keep keys up-to-date; MUST be run before the #listen_task,
    1135              :      hence with priority. */
    1136           18 :   keygen_task = GNUNET_SCHEDULER_add_with_priority (
    1137              :     GNUNET_SCHEDULER_PRIORITY_URGENT,
    1138              :     &update_keys,
    1139              :     NULL);
    1140              : }
        

Generated by: LCOV version 2.0-1