LCOV - code coverage report
Current view: top level - util - secmod_eddsa.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 246 383 64.2 %
Date: 2025-06-05 21:03:14 Functions: 17 17 100.0 %

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

Generated by: LCOV version 1.16