LCOV - code coverage report
Current view: top level - util - taler-exchange-secmod-cs.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 343 532 64.5 %
Date: 2022-08-25 06:15:09 Functions: 19 21 90.5 %
Legend: Lines: hit not hit

          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/taler-exchange-secmod-cs.c
      18             :  * @brief Standalone process to perform private key CS 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 thread 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 do the signing in parallel, one per client.
      29             :  * - thread-safety: signing happens in parallel, thus when REMOVING private keys,
      30             :  *   we must ensure that all signers are done before we fully free() the
      31             :  *   private key. This is done by reference counting (as work is always
      32             :  *   assigned and collected by the main thread).
      33             :  */
      34             : #include "platform.h"
      35             : #include "taler_util.h"
      36             : #include "taler-exchange-secmod-cs.h"
      37             : #include <gcrypt.h>
      38             : #include <pthread.h>
      39             : #include <sys/eventfd.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             :  * Information we keep per denomination.
      48             :  */
      49             : struct Denomination;
      50             : 
      51             : 
      52             : /**
      53             :  * One particular denomination key.
      54             :  */
      55             : struct DenominationKey
      56             : {
      57             : 
      58             :   /**
      59             :    * Kept in a DLL of the respective denomination. Sorted by anchor time.
      60             :    */
      61             :   struct DenominationKey *next;
      62             : 
      63             :   /**
      64             :    * Kept in a DLL of the respective denomination. Sorted by anchor time.
      65             :    */
      66             :   struct DenominationKey *prev;
      67             : 
      68             :   /**
      69             :    * Denomination this key belongs to.
      70             :    */
      71             :   struct Denomination *denom;
      72             : 
      73             :   /**
      74             :    * Name of the file this key is stored under.
      75             :    */
      76             :   char *filename;
      77             : 
      78             :   /**
      79             :    * The private key of the denomination.
      80             :    */
      81             :   struct GNUNET_CRYPTO_CsPrivateKey denom_priv;
      82             : 
      83             :   /**
      84             :    * The public key of the denomination.
      85             :    */
      86             :   struct GNUNET_CRYPTO_CsPublicKey denom_pub;
      87             : 
      88             :   /**
      89             :    * Message to transmit to clients to introduce this public key.
      90             :    */
      91             :   struct TALER_CRYPTO_CsKeyAvailableNotification *an;
      92             : 
      93             :   /**
      94             :    * Hash of this denomination's public key.
      95             :    */
      96             :   struct TALER_CsPubHashP h_cs;
      97             : 
      98             :   /**
      99             :    * Time at which this key is supposed to become valid.
     100             :    */
     101             :   struct GNUNET_TIME_Timestamp anchor;
     102             : 
     103             :   /**
     104             :    * Generation when this key was created or revoked.
     105             :    */
     106             :   uint64_t key_gen;
     107             : 
     108             :   /**
     109             :    * Reference counter. Counts the number of threads that are
     110             :    * using this key at this time.
     111             :    */
     112             :   unsigned int rc;
     113             : 
     114             :   /**
     115             :    * Flag set to true if this key has been purged and the memory
     116             :    * must be freed as soon as @e rc hits zero.
     117             :    */
     118             :   bool purge;
     119             : 
     120             : };
     121             : 
     122             : 
     123             : struct Denomination
     124             : {
     125             : 
     126             :   /**
     127             :    * Kept in a DLL. Sorted by #denomination_action_time().
     128             :    */
     129             :   struct Denomination *next;
     130             : 
     131             :   /**
     132             :    * Kept in a DLL. Sorted by #denomination_action_time().
     133             :    */
     134             :   struct Denomination *prev;
     135             : 
     136             :   /**
     137             :    * Head of DLL of actual keys of this denomination.
     138             :    */
     139             :   struct DenominationKey *keys_head;
     140             : 
     141             :   /**
     142             :    * Tail of DLL of actual keys of this denomination.
     143             :    */
     144             :   struct DenominationKey *keys_tail;
     145             : 
     146             :   /**
     147             :    * How long can coins be withdrawn (generated)?  Should be small
     148             :    * enough to limit how many coins will be signed into existence with
     149             :    * the same key, but large enough to still provide a reasonable
     150             :    * anonymity set.
     151             :    */
     152             :   struct GNUNET_TIME_Relative duration_withdraw;
     153             : 
     154             :   /**
     155             :    * What is the configuration section of this denomination type?  Also used
     156             :    * for the directory name where the denomination keys are stored.
     157             :    */
     158             :   char *section;
     159             : 
     160             : };
     161             : 
     162             : 
     163             : /**
     164             :  * Return value from main().
     165             :  */
     166             : static int global_ret;
     167             : 
     168             : /**
     169             :  * Time when the key update is executed.
     170             :  * Either the actual current time, or a pretended time.
     171             :  */
     172             : static struct GNUNET_TIME_Timestamp now;
     173             : 
     174             : /**
     175             :  * The time for the key update, as passed by the user
     176             :  * on the command line.
     177             :  */
     178             : static struct GNUNET_TIME_Timestamp now_tmp;
     179             : 
     180             : /**
     181             :  * Where do we store the keys?
     182             :  */
     183             : static char *keydir;
     184             : 
     185             : /**
     186             :  * How much should coin creation (@e duration_withdraw) duration overlap
     187             :  * with the next denomination?  Basically, the starting time of two
     188             :  * denominations is always @e duration_withdraw - #overlap_duration apart.
     189             :  */
     190             : static struct GNUNET_TIME_Relative overlap_duration;
     191             : 
     192             : /**
     193             :  * How long into the future do we pre-generate keys?
     194             :  */
     195             : static struct GNUNET_TIME_Relative lookahead_sign;
     196             : 
     197             : /**
     198             :  * All of our denominations, in a DLL. Sorted?
     199             :  */
     200             : static struct Denomination *denom_head;
     201             : 
     202             : /**
     203             :  * All of our denominations, in a DLL. Sorted?
     204             :  */
     205             : static struct Denomination *denom_tail;
     206             : 
     207             : /**
     208             :  * Map of hashes of public (CS) keys to `struct DenominationKey *`
     209             :  * with the respective private keys.
     210             :  */
     211             : static struct GNUNET_CONTAINER_MultiHashMap *keys;
     212             : 
     213             : /**
     214             :  * Task run to generate new keys.
     215             :  */
     216             : static struct GNUNET_SCHEDULER_Task *keygen_task;
     217             : 
     218             : /**
     219             :  * Lock for the keys queue.
     220             :  */
     221             : static pthread_mutex_t keys_lock;
     222             : 
     223             : /**
     224             :  * Current key generation.
     225             :  */
     226             : static uint64_t key_gen;
     227             : 
     228             : 
     229             : /**
     230             :  * Generate the announcement message for @a dk.
     231             :  *
     232             :  * @param[in,out] dk denomination key to generate the announcement for
     233             :  */
     234             : static void
     235           9 : generate_response (struct DenominationKey *dk)
     236             : {
     237           9 :   struct Denomination *denom = dk->denom;
     238           9 :   size_t nlen = strlen (denom->section) + 1;
     239             :   struct TALER_CRYPTO_CsKeyAvailableNotification *an;
     240             :   void *p;
     241             :   size_t tlen;
     242             : 
     243             :   GNUNET_assert (sizeof(dk->denom_pub) < UINT16_MAX);
     244           9 :   GNUNET_assert (nlen < UINT16_MAX);
     245           9 :   tlen = nlen + sizeof (*an);
     246           9 :   GNUNET_assert (tlen < UINT16_MAX);
     247           9 :   an = GNUNET_malloc (tlen);
     248           9 :   an->header.size = htons ((uint16_t) tlen);
     249           9 :   an->header.type = htons (TALER_HELPER_CS_MT_AVAIL);
     250           9 :   an->section_name_len = htons ((uint16_t) nlen);
     251           9 :   an->anchor_time = GNUNET_TIME_timestamp_hton (dk->anchor);
     252           9 :   an->duration_withdraw = GNUNET_TIME_relative_hton (denom->duration_withdraw);
     253           9 :   an->denom_pub = dk->denom_pub;
     254           9 :   TALER_exchange_secmod_cs_sign (&dk->h_cs,
     255           9 :                                  denom->section,
     256             :                                  dk->anchor,
     257             :                                  denom->duration_withdraw,
     258             :                                  &TES_smpriv,
     259             :                                  &an->secm_sig);
     260           9 :   an->secm_pub = TES_smpub;
     261           9 :   p = (void *) &an[1];
     262           9 :   memcpy (p,
     263           9 :           denom->section,
     264             :           nlen);
     265           9 :   dk->an = an;
     266           9 : }
     267             : 
     268             : 
     269             : /**
     270             :  * Handle @a client request @a sr to create signature. Create the
     271             :  * signature using the respective key and return the result to
     272             :  * the client.
     273             :  *
     274             :  * @param client the client making the request
     275             :  * @param sr the request details
     276             :  * @return #GNUNET_OK on success
     277             :  */
     278             : static enum GNUNET_GenericReturnValue
     279         902 : handle_sign_request (struct TES_Client *client,
     280             :                      const struct TALER_CRYPTO_CsSignRequest *sr)
     281             : {
     282             :   struct DenominationKey *dk;
     283             :   struct GNUNET_CRYPTO_CsRSecret r[2];
     284             :   struct TALER_BlindedDenominationCsSignAnswer cs_answer;
     285         902 :   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
     286             :   bool for_melt;
     287             : 
     288         902 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     289         902 :   dk = GNUNET_CONTAINER_multihashmap_get (keys,
     290             :                                           &sr->h_cs.hash);
     291         902 :   if (NULL == dk)
     292             :   {
     293           1 :     struct TALER_CRYPTO_SignFailure sf = {
     294           1 :       .header.size = htons (sizeof (sr)),
     295           1 :       .header.type = htons (TALER_HELPER_CS_MT_RES_SIGN_FAILURE),
     296           1 :       .ec = htonl (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN)
     297             :     };
     298             : 
     299           1 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     300           1 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     301             :                 "Signing request failed, denomination key %s unknown\n",
     302             :                 GNUNET_h2s (&sr->h_cs.hash));
     303           1 :     return TES_transmit (client->csock,
     304             :                          &sf.header);
     305             :   }
     306         901 :   if (GNUNET_TIME_absolute_is_future (dk->anchor.abs_time))
     307             :   {
     308             :     /* it is too early */
     309           0 :     struct TALER_CRYPTO_SignFailure sf = {
     310           0 :       .header.size = htons (sizeof (sr)),
     311           0 :       .header.type = htons (TALER_HELPER_CS_MT_RES_SIGN_FAILURE),
     312           0 :       .ec = htonl (TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY)
     313             :     };
     314             : 
     315           0 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     316           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     317             :                 "Signing request failed, denomination key %s is not yet valid\n",
     318             :                 GNUNET_h2s (&sr->h_cs.hash));
     319           0 :     return TES_transmit (client->csock,
     320             :                          &sf.header);
     321             :   }
     322         901 :   for_melt = (0 != ntohl (sr->for_melt));
     323         901 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     324             :               "Received request to sign over bytes with key %s\n",
     325             :               GNUNET_h2s (&sr->h_cs.hash));
     326         901 :   GNUNET_assert (dk->rc < UINT_MAX);
     327         901 :   dk->rc++;
     328         901 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     329         901 :   GNUNET_CRYPTO_cs_r_derive (&sr->planchet.nonce.nonce,
     330             :                              for_melt ? "rm" : "rw",
     331         901 :                              &dk->denom_priv,
     332             :                              r);
     333        1802 :   cs_answer.b = GNUNET_CRYPTO_cs_sign_derive (&dk->denom_priv,
     334             :                                               r,
     335         901 :                                               sr->planchet.c,
     336             :                                               &sr->planchet.nonce.nonce,
     337             :                                               &cs_answer.s_scalar);
     338             : 
     339         901 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     340         901 :   GNUNET_assert (dk->rc > 0);
     341         901 :   dk->rc--;
     342         901 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     343             :   // if (NULL == cs_answer)
     344             :   // {
     345             :   //   struct TALER_CRYPTO_SignFailure sf = {
     346             :   //     .header.size = htons (sizeof (sf)),
     347             :   //     .header.type = htons (TALER_HELPER_CS_MT_RES_SIGN_FAILURE),
     348             :   //     .ec = htonl (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE)
     349             :   //   };
     350             : 
     351             :   //   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     352             :   //               "Signing request failed, worker failed to produce signature\n");
     353             :   //   return TES_transmit (client->csock,
     354             :   //                        &sf.header);
     355             :   // }
     356             : 
     357             :   {
     358             :     struct TALER_CRYPTO_SignResponse *sr;
     359             :     size_t tsize;
     360             :     enum GNUNET_GenericReturnValue ret;
     361             : 
     362         901 :     tsize = sizeof (*sr) + sizeof(cs_answer);
     363         901 :     GNUNET_assert (tsize < UINT16_MAX);
     364         901 :     sr = GNUNET_malloc (tsize);
     365         901 :     sr->header.size = htons (tsize);
     366         901 :     sr->header.type = htons (TALER_HELPER_CS_MT_RES_SIGNATURE);
     367         901 :     sr->cs_answer = cs_answer;
     368         901 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     369             :                 "Sending CS signature after %s\n",
     370             :                 GNUNET_TIME_relative2s (
     371             :                   GNUNET_TIME_absolute_get_duration (now),
     372             :                   GNUNET_YES));
     373         901 :     ret = TES_transmit (client->csock,
     374         901 :                         &sr->header);
     375         901 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     376             :                 "Sent CS signature after %s\n",
     377             :                 GNUNET_TIME_relative2s (
     378             :                   GNUNET_TIME_absolute_get_duration (now),
     379             :                   GNUNET_YES));
     380         900 :     GNUNET_free (sr);
     381         900 :     return ret;
     382             :   }
     383             : }
     384             : 
     385             : 
     386             : /**
     387             :  * Initialize key material for denomination key @a dk (also on disk).
     388             :  *
     389             :  * @param[in,out] dk denomination key to compute key material for
     390             :  * @param position where in the DLL will the @a dk go
     391             :  * @return #GNUNET_OK on success
     392             :  */
     393             : static enum GNUNET_GenericReturnValue
     394           9 : setup_key (struct DenominationKey *dk,
     395             :            struct DenominationKey *position)
     396             : {
     397           9 :   struct Denomination *denom = dk->denom;
     398             :   struct GNUNET_CRYPTO_CsPrivateKey priv;
     399             :   struct GNUNET_CRYPTO_CsPublicKey pub;
     400             : 
     401           9 :   GNUNET_CRYPTO_cs_private_key_generate (&priv);
     402           9 :   GNUNET_CRYPTO_cs_private_key_get_public (&priv,
     403             :                                            &pub);
     404           9 :   TALER_cs_pub_hash (&pub,
     405             :                      &dk->h_cs);
     406           9 :   GNUNET_asprintf (&dk->filename,
     407             :                    "%s/%s/%llu",
     408             :                    keydir,
     409             :                    denom->section,
     410           9 :                    (unsigned long long) (dk->anchor.abs_time.abs_value_us
     411           9 :                                          / GNUNET_TIME_UNIT_SECONDS.rel_value_us));
     412           9 :   if (GNUNET_OK !=
     413           9 :       GNUNET_DISK_fn_write (dk->filename,
     414             :                             &priv,
     415             :                             sizeof(priv),
     416             :                             GNUNET_DISK_PERM_USER_READ))
     417             :   {
     418           0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     419             :                               "write",
     420             :                               dk->filename);
     421           0 :     return GNUNET_SYSERR;
     422             :   }
     423           9 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     424             :               "Setup fresh private key %s at %s in `%s' (generation #%llu)\n",
     425             :               GNUNET_h2s (&dk->h_cs.hash),
     426             :               GNUNET_TIME_timestamp2s (dk->anchor),
     427             :               dk->filename,
     428             :               (unsigned long long) key_gen);
     429           9 :   dk->denom_priv = priv;
     430           9 :   dk->denom_pub = pub;
     431           9 :   dk->key_gen = key_gen;
     432           9 :   generate_response (dk);
     433           9 :   if (GNUNET_OK !=
     434           9 :       GNUNET_CONTAINER_multihashmap_put (
     435             :         keys,
     436           9 :         &dk->h_cs.hash,
     437             :         dk,
     438             :         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
     439             :   {
     440           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     441             :                 "Duplicate private key created! Terminating.\n");
     442           0 :     GNUNET_free (dk->filename);
     443           0 :     GNUNET_free (dk->an);
     444           0 :     GNUNET_free (dk);
     445           0 :     return GNUNET_SYSERR;
     446             :   }
     447           9 :   GNUNET_CONTAINER_DLL_insert_after (denom->keys_head,
     448             :                                      denom->keys_tail,
     449             :                                      position,
     450             :                                      dk);
     451           9 :   return GNUNET_OK;
     452             : }
     453             : 
     454             : 
     455             : /**
     456             :  * The withdraw period of a key @a dk has expired. Purge it.
     457             :  *
     458             :  * @param[in] dk expired denomination key to purge
     459             :  */
     460             : static void
     461           3 : purge_key (struct DenominationKey *dk)
     462             : {
     463           3 :   if (dk->purge)
     464           0 :     return;
     465           3 :   if (0 != unlink (dk->filename))
     466           0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     467             :                               "unlink",
     468             :                               dk->filename);
     469           3 :   GNUNET_free (dk->filename);
     470           3 :   dk->purge = true;
     471           3 :   dk->key_gen = key_gen;
     472             : }
     473             : 
     474             : 
     475             : /**
     476             :  * A @a client informs us that a key has been revoked.
     477             :  * Check if the key is still in use, and if so replace (!)
     478             :  * it with a fresh key.
     479             :  *
     480             :  * @param client the client making the request
     481             :  * @param rr the revocation request
     482             :  */
     483             : static enum GNUNET_GenericReturnValue
     484           3 : handle_revoke_request (struct TES_Client *client,
     485             :                        const struct TALER_CRYPTO_CsRevokeRequest *rr)
     486             : {
     487             :   struct DenominationKey *dk;
     488             :   struct DenominationKey *ndk;
     489             :   struct Denomination *denom;
     490             : 
     491             :   (void) client;
     492           3 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     493           3 :   dk = GNUNET_CONTAINER_multihashmap_get (keys,
     494             :                                           &rr->h_cs.hash);
     495           3 :   if (NULL == dk)
     496             :   {
     497           0 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     498           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     499             :                 "Revocation request ignored, denomination key %s unknown\n",
     500             :                 GNUNET_h2s (&rr->h_cs.hash));
     501           0 :     return GNUNET_OK;
     502             :   }
     503           3 :   if (dk->purge)
     504             :   {
     505           0 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     506           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     507             :                 "Revocation request ignored, denomination key %s already revoked\n",
     508             :                 GNUNET_h2s (&rr->h_cs.hash));
     509           0 :     return GNUNET_OK;
     510             :   }
     511             : 
     512           3 :   key_gen++;
     513           3 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     514             :               "Revoking key %s, bumping generation to %llu\n",
     515             :               GNUNET_h2s (&rr->h_cs.hash),
     516             :               (unsigned long long) key_gen);
     517           3 :   purge_key (dk);
     518             : 
     519             :   /* Setup replacement key */
     520           3 :   denom = dk->denom;
     521           3 :   ndk = GNUNET_new (struct DenominationKey);
     522           3 :   ndk->denom = denom;
     523           3 :   ndk->anchor = dk->anchor;
     524           3 :   if (GNUNET_OK !=
     525           3 :       setup_key (ndk,
     526             :                  dk))
     527             :   {
     528           0 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     529           0 :     GNUNET_break (0);
     530           0 :     GNUNET_SCHEDULER_shutdown ();
     531           0 :     global_ret = EXIT_FAILURE;
     532           0 :     return GNUNET_SYSERR;
     533             :   }
     534           3 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     535           3 :   TES_wake_clients ();
     536           3 :   return GNUNET_OK;
     537             : }
     538             : 
     539             : 
     540             : /**
     541             :  * Handle @a client request @a rdr to create signature. Create the
     542             :  * signature using the respective key and return the result to
     543             :  * the client.
     544             :  *
     545             :  * @param client the client making the request
     546             :  * @param rdr the request details
     547             :  * @return #GNUNET_OK on success
     548             :  */
     549             : static enum GNUNET_GenericReturnValue
     550          22 : handle_r_derive_request (struct TES_Client *client,
     551             :                          const struct TALER_CRYPTO_CsRDeriveRequest *rdr)
     552             : {
     553             :   struct DenominationKey *dk;
     554             :   struct TALER_DenominationCSPrivateRPairP r_priv;
     555             :   struct TALER_DenominationCSPublicRPairP r_pub;
     556          22 :   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
     557             :   bool for_melt;
     558             : 
     559          22 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     560          22 :   dk = GNUNET_CONTAINER_multihashmap_get (keys,
     561             :                                           &rdr->h_cs.hash);
     562          22 :   if (NULL == dk)
     563             :   {
     564           1 :     struct TALER_CRYPTO_RDeriveFailure rdf = {
     565           1 :       .header.size = htons (sizeof (rdr)),
     566           1 :       .header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE),
     567           1 :       .ec = htonl (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN)
     568             :     };
     569             : 
     570           1 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     571           1 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     572             :                 "R Derive request failed, denomination key %s unknown\n",
     573             :                 GNUNET_h2s (&rdr->h_cs.hash));
     574           1 :     return TES_transmit (client->csock,
     575             :                          &rdf.header);
     576             :   }
     577          21 :   if (GNUNET_TIME_absolute_is_future (dk->anchor.abs_time))
     578             :   {
     579             :     /* it is too early */
     580          10 :     struct TALER_CRYPTO_RDeriveFailure rdf = {
     581          10 :       .header.size = htons (sizeof (rdr)),
     582          10 :       .header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE),
     583          10 :       .ec = htonl (TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY)
     584             :     };
     585             : 
     586          10 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     587          10 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     588             :                 "R Derive request failed, denomination key %s is not yet valid\n",
     589             :                 GNUNET_h2s (&rdr->h_cs.hash));
     590          10 :     return TES_transmit (client->csock,
     591             :                          &rdf.header);
     592             :   }
     593          11 :   for_melt = (0 != ntohl (rdr->for_melt));
     594          11 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     595             :               "Received request to derive R with key %s\n",
     596             :               GNUNET_h2s (&rdr->h_cs.hash));
     597          11 :   GNUNET_assert (dk->rc < UINT_MAX);
     598          11 :   dk->rc++;
     599          11 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     600          11 :   GNUNET_CRYPTO_cs_r_derive (&rdr->nonce.nonce,
     601             :                              for_melt ? "rm" : "rw",
     602          11 :                              &dk->denom_priv,
     603             :                              r_priv.r);
     604          11 :   GNUNET_CRYPTO_cs_r_get_public (&r_priv.r[0],
     605             :                                  &r_pub.r_pub[0]);
     606          11 :   GNUNET_CRYPTO_cs_r_get_public (&r_priv.r[1],
     607             :                                  &r_pub.r_pub[1]);
     608          11 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     609          11 :   GNUNET_assert (dk->rc > 0);
     610          11 :   dk->rc--;
     611          11 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     612             : 
     613             :   {
     614          11 :     struct TALER_CRYPTO_RDeriveResponse rdr = {
     615          11 :       .header.size = htons (sizeof (struct TALER_CRYPTO_RDeriveResponse)),
     616          11 :       .header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE),
     617             :       .r_pub = r_pub
     618             :     };
     619             :     enum GNUNET_GenericReturnValue ret;
     620             : 
     621          11 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     622             :                 "Sending CS Derived R after %s\n",
     623             :                 GNUNET_TIME_relative2s (
     624             :                   GNUNET_TIME_absolute_get_duration (now),
     625             :                   GNUNET_YES));
     626          11 :     ret = TES_transmit (client->csock,
     627             :                         &rdr.header);
     628          11 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     629             :                 "Sent CS Derived R after %s\n",
     630             :                 GNUNET_TIME_relative2s (
     631             :                   GNUNET_TIME_absolute_get_duration (now),
     632             :                   GNUNET_YES));
     633          11 :     return ret;
     634             :   }
     635             : }
     636             : 
     637             : 
     638             : /**
     639             :  * Handle @a hdr message received from @a client.
     640             :  *
     641             :  * @param client the client that received the message
     642             :  * @param hdr message that was received
     643             :  * @return #GNUNET_OK on success
     644             :  */
     645             : static enum GNUNET_GenericReturnValue
     646         927 : cs_work_dispatch (struct TES_Client *client,
     647             :                   const struct GNUNET_MessageHeader *hdr)
     648             : {
     649         927 :   uint16_t msize = ntohs (hdr->size);
     650             : 
     651         927 :   switch (ntohs (hdr->type))
     652             :   {
     653         902 :   case TALER_HELPER_CS_MT_REQ_SIGN:
     654         902 :     if (msize < sizeof (struct TALER_CRYPTO_CsSignRequest))
     655             :     {
     656           0 :       GNUNET_break_op (0);
     657           0 :       return GNUNET_SYSERR;
     658             :     }
     659         902 :     return handle_sign_request (
     660             :       client,
     661             :       (const struct TALER_CRYPTO_CsSignRequest *) hdr);
     662           3 :   case TALER_HELPER_CS_MT_REQ_REVOKE:
     663           3 :     if (msize != sizeof (struct TALER_CRYPTO_CsRevokeRequest))
     664             :     {
     665           0 :       GNUNET_break_op (0);
     666           0 :       return GNUNET_SYSERR;
     667             :     }
     668           3 :     return handle_revoke_request (
     669             :       client,
     670             :       (const struct TALER_CRYPTO_CsRevokeRequest *) hdr);
     671          22 :   case TALER_HELPER_CS_MT_REQ_RDERIVE:
     672          22 :     if (msize != sizeof (struct TALER_CRYPTO_CsRDeriveRequest))
     673             :     {
     674           0 :       GNUNET_break_op (0);
     675           0 :       return GNUNET_SYSERR;
     676             :     }
     677          22 :     return handle_r_derive_request (client,
     678             :                                     (const struct
     679             :                                      TALER_CRYPTO_CsRDeriveRequest *) hdr);
     680           0 :   default:
     681           0 :     GNUNET_break_op (0);
     682           0 :     return GNUNET_SYSERR;
     683             :   }
     684             : }
     685             : 
     686             : 
     687             : /**
     688             :  * Send our initial key set to @a client together with the
     689             :  * "sync" terminator.
     690             :  *
     691             :  * @param client the client to inform
     692             :  * @return #GNUNET_OK on success
     693             :  */
     694             : static enum GNUNET_GenericReturnValue
     695           9 : cs_client_init (struct TES_Client *client)
     696             : {
     697           9 :   size_t obs = 0;
     698             :   char *buf;
     699             : 
     700           9 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     701             :               "Initializing new client %p\n",
     702             :               client);
     703           9 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     704          18 :   for (struct Denomination *denom = denom_head;
     705             :        NULL != denom;
     706           9 :        denom = denom->next)
     707             :   {
     708          87 :     for (struct DenominationKey *dk = denom->keys_head;
     709             :          NULL != dk;
     710          78 :          dk = dk->next)
     711             :     {
     712          78 :       obs += ntohs (dk->an->header.size);
     713             :     }
     714             :   }
     715           9 :   buf = GNUNET_malloc (obs);
     716           9 :   obs = 0;
     717          18 :   for (struct Denomination *denom = denom_head;
     718             :        NULL != denom;
     719           9 :        denom = denom->next)
     720             :   {
     721          87 :     for (struct DenominationKey *dk = denom->keys_head;
     722             :          NULL != dk;
     723          78 :          dk = dk->next)
     724             :     {
     725          78 :       memcpy (&buf[obs],
     726          78 :               dk->an,
     727          78 :               ntohs (dk->an->header.size));
     728          78 :       obs += ntohs (dk->an->header.size);
     729             :     }
     730             :   }
     731           9 :   client->key_gen = key_gen;
     732           9 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     733           9 :   if (GNUNET_OK !=
     734           9 :       TES_transmit_raw (client->csock,
     735             :                         obs,
     736             :                         buf))
     737             :   {
     738           0 :     GNUNET_free (buf);
     739           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     740             :                 "Client %p must have disconnected\n",
     741             :                 client);
     742           0 :     return GNUNET_SYSERR;
     743             :   }
     744           9 :   GNUNET_free (buf);
     745             :   {
     746           9 :     struct GNUNET_MessageHeader synced = {
     747           9 :       .type = htons (TALER_HELPER_CS_SYNCED),
     748           9 :       .size = htons (sizeof (synced))
     749             :     };
     750             : 
     751           9 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     752             :                 "Sending CS SYNCED message to %p\n",
     753             :                 client);
     754           9 :     if (GNUNET_OK !=
     755           9 :         TES_transmit (client->csock,
     756             :                       &synced))
     757             :     {
     758           0 :       GNUNET_break (0);
     759           0 :       return GNUNET_SYSERR;
     760             :     }
     761             :   }
     762           9 :   return GNUNET_OK;
     763             : }
     764             : 
     765             : 
     766             : /**
     767             :  * Notify @a client about all changes to the keys since
     768             :  * the last generation known to the @a client.
     769             :  *
     770             :  * @param client the client to notify
     771             :  * @return #GNUNET_OK on success
     772             :  */
     773             : static enum GNUNET_GenericReturnValue
     774           3 : cs_update_client_keys (struct TES_Client *client)
     775             : {
     776           3 :   size_t obs = 0;
     777             :   char *buf;
     778             :   enum GNUNET_GenericReturnValue ret;
     779             : 
     780           3 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     781           6 :   for (struct Denomination *denom = denom_head;
     782             :        NULL != denom;
     783           3 :        denom = denom->next)
     784             :   {
     785          27 :     for (struct DenominationKey *key = denom->keys_head;
     786             :          NULL != key;
     787          24 :          key = key->next)
     788             :     {
     789          24 :       if (key->key_gen <= client->key_gen)
     790          18 :         continue;
     791           6 :       if (key->purge)
     792           3 :         obs += sizeof (struct TALER_CRYPTO_CsKeyPurgeNotification);
     793             :       else
     794           3 :         obs += ntohs (key->an->header.size);
     795             :     }
     796             :   }
     797           3 :   if (0 == obs)
     798             :   {
     799             :     /* nothing to do */
     800           0 :     client->key_gen = key_gen;
     801           0 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     802           0 :     return GNUNET_OK;
     803             :   }
     804           3 :   buf = GNUNET_malloc (obs);
     805           3 :   obs = 0;
     806           6 :   for (struct Denomination *denom = denom_head;
     807             :        NULL != denom;
     808           3 :        denom = denom->next)
     809             :   {
     810          27 :     for (struct DenominationKey *key = denom->keys_head;
     811             :          NULL != key;
     812          24 :          key = key->next)
     813             :     {
     814          24 :       if (key->key_gen <= client->key_gen)
     815          18 :         continue;
     816           6 :       if (key->purge)
     817             :       {
     818           3 :         struct TALER_CRYPTO_CsKeyPurgeNotification pn = {
     819           3 :           .header.type = htons (TALER_HELPER_CS_MT_PURGE),
     820           3 :           .header.size = htons (sizeof (pn)),
     821             :           .h_cs = key->h_cs
     822             :         };
     823             : 
     824           3 :         memcpy (&buf[obs],
     825             :                 &pn,
     826             :                 sizeof (pn));
     827           3 :         GNUNET_assert (obs + sizeof (pn)
     828             :                        > obs);
     829           3 :         obs += sizeof (pn);
     830             :       }
     831             :       else
     832             :       {
     833           3 :         memcpy (&buf[obs],
     834           3 :                 key->an,
     835           3 :                 ntohs (key->an->header.size));
     836           3 :         GNUNET_assert (obs + ntohs (key->an->header.size)
     837             :                        > obs);
     838           3 :         obs += ntohs (key->an->header.size);
     839             :       }
     840             :     }
     841             :   }
     842           3 :   client->key_gen = key_gen;
     843           3 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     844           3 :   ret = TES_transmit_raw (client->csock,
     845             :                           obs,
     846             :                           buf);
     847           3 :   GNUNET_free (buf);
     848           3 :   return ret;
     849             : }
     850             : 
     851             : 
     852             : /**
     853             :  * Create a new denomination key (we do not have enough).
     854             :  *
     855             :  * @param denom denomination key to create
     856             :  * @param now current time to use (to get many keys to use the exact same time)
     857             :  * @return #GNUNET_OK on success
     858             :  */
     859             : static enum GNUNET_GenericReturnValue
     860           6 : create_key (struct Denomination *denom,
     861             :             struct GNUNET_TIME_Timestamp now)
     862             : {
     863             :   struct DenominationKey *dk;
     864             :   struct GNUNET_TIME_Timestamp anchor;
     865             : 
     866           6 :   anchor = now;
     867           6 :   if (NULL != denom->keys_tail)
     868             :   {
     869             :     struct GNUNET_TIME_Absolute abs;
     870             : 
     871           5 :     abs = GNUNET_TIME_absolute_add (denom->keys_tail->anchor.abs_time,
     872             :                                     GNUNET_TIME_relative_subtract (
     873             :                                       denom->duration_withdraw,
     874             :                                       overlap_duration));
     875           5 :     if (GNUNET_TIME_absolute_cmp (now.abs_time, <, abs))
     876           5 :       anchor = GNUNET_TIME_absolute_to_timestamp (abs);
     877             :   }
     878           6 :   dk = GNUNET_new (struct DenominationKey);
     879           6 :   dk->denom = denom;
     880           6 :   dk->anchor = anchor;
     881           6 :   if (GNUNET_OK !=
     882           6 :       setup_key (dk,
     883             :                  denom->keys_tail))
     884             :   {
     885           0 :     GNUNET_break (0);
     886           0 :     GNUNET_free (dk);
     887           0 :     GNUNET_SCHEDULER_shutdown ();
     888           0 :     global_ret = EXIT_FAILURE;
     889           0 :     return GNUNET_SYSERR;
     890             :   }
     891           6 :   return GNUNET_OK;
     892             : }
     893             : 
     894             : 
     895             : /**
     896             :  * At what time does this denomination require its next action?
     897             :  * Basically, the minimum of the withdraw expiration time of the
     898             :  * oldest denomination key, and the withdraw expiration time of
     899             :  * the newest denomination key minus the #lookahead_sign time.
     900             :  *
     901             :  * @param denom denomination to compute action time for
     902             :  */
     903             : static struct GNUNET_TIME_Absolute
     904           3 : denomination_action_time (const struct Denomination *denom)
     905             : {
     906           3 :   struct DenominationKey *head = denom->keys_head;
     907           3 :   struct DenominationKey *tail = denom->keys_tail;
     908             :   struct GNUNET_TIME_Absolute tt;
     909             : 
     910           3 :   if (NULL == head)
     911           0 :     return GNUNET_TIME_UNIT_ZERO_ABS;
     912           3 :   tt = GNUNET_TIME_absolute_subtract (
     913             :     GNUNET_TIME_absolute_subtract (
     914             :       GNUNET_TIME_absolute_add (tail->anchor.abs_time,
     915             :                                 denom->duration_withdraw),
     916             :       lookahead_sign),
     917             :     overlap_duration);
     918           3 :   if (head->rc > 0)
     919           0 :     return tt; /* head expiration does not count due to rc > 0 */
     920           3 :   return GNUNET_TIME_absolute_min (
     921             :     GNUNET_TIME_absolute_add (head->anchor.abs_time,
     922             :                               denom->duration_withdraw),
     923             :     tt);
     924             : }
     925             : 
     926             : 
     927             : /**
     928             :  * Create new keys and expire ancient keys of the given denomination @a denom.
     929             :  * Removes the @a denom from the #denom_head DLL and re-insert its at the
     930             :  * correct location sorted by next maintenance activity.
     931             :  *
     932             :  * @param[in,out] denom denomination to update material for
     933             :  * @param now current time to use (to get many keys to use the exact same time)
     934             :  * @param[in,out] wake set to true if we should wake the clients
     935             :  * @return #GNUNET_OK on success
     936             :  */
     937             : static enum GNUNET_GenericReturnValue
     938           2 : update_keys (struct Denomination *denom,
     939             :              struct GNUNET_TIME_Timestamp now,
     940             :              bool *wake)
     941             : {
     942             :   /* create new denomination keys */
     943           2 :   if (NULL != denom->keys_tail)
     944           1 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     945             :                 "Updating keys of denomination `%s', last key %s valid for another %s\n",
     946             :                 denom->section,
     947             :                 GNUNET_h2s (&denom->keys_tail->h_cs.hash),
     948             :                 GNUNET_TIME_relative2s (
     949             :                   GNUNET_TIME_absolute_get_remaining (
     950             :                     GNUNET_TIME_absolute_subtract (
     951             :                       GNUNET_TIME_absolute_add (
     952             :                         denom->keys_tail->anchor.abs_time,
     953             :                         denom->duration_withdraw),
     954             :                       overlap_duration)),
     955             :                   GNUNET_YES));
     956          15 :   while ( (NULL == denom->keys_tail) ||
     957           7 :           GNUNET_TIME_absolute_is_past (
     958             :             GNUNET_TIME_absolute_subtract (
     959             :               GNUNET_TIME_absolute_subtract (
     960           7 :                 GNUNET_TIME_absolute_add (denom->keys_tail->anchor.abs_time,
     961             :                                           denom->duration_withdraw),
     962             :                 lookahead_sign),
     963             :               overlap_duration)) )
     964             :   {
     965           6 :     if (! *wake)
     966             :     {
     967           0 :       key_gen++;
     968           0 :       *wake = true;
     969             :     }
     970           6 :     if (GNUNET_OK !=
     971           6 :         create_key (denom,
     972             :                     now))
     973             :     {
     974           0 :       GNUNET_break (0);
     975           0 :       global_ret = EXIT_FAILURE;
     976           0 :       GNUNET_SCHEDULER_shutdown ();
     977           0 :       return GNUNET_SYSERR;
     978             :     }
     979             :   }
     980             :   /* remove expired denomination keys */
     981           4 :   while ( (NULL != denom->keys_head) &&
     982           2 :           GNUNET_TIME_absolute_is_past
     983           2 :             (GNUNET_TIME_absolute_add (denom->keys_head->anchor.abs_time,
     984             :                                        denom->duration_withdraw)) )
     985             :   {
     986           0 :     struct DenominationKey *key = denom->keys_head;
     987           0 :     struct DenominationKey *nxt = key->next;
     988             : 
     989           0 :     if (0 != key->rc)
     990           0 :       break; /* later */
     991           0 :     GNUNET_CONTAINER_DLL_remove (denom->keys_head,
     992             :                                  denom->keys_tail,
     993             :                                  key);
     994           0 :     GNUNET_assert (GNUNET_OK ==
     995             :                    GNUNET_CONTAINER_multihashmap_remove (
     996             :                      keys,
     997             :                      &key->h_cs.hash,
     998             :                      key));
     999           0 :     if ( (! key->purge) &&
    1000           0 :          (0 != unlink (key->filename)) )
    1001           0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    1002             :                                 "unlink",
    1003             :                                 key->filename);
    1004           0 :     GNUNET_free (key->filename);
    1005           0 :     GNUNET_free (key->an);
    1006           0 :     GNUNET_free (key);
    1007           0 :     key = nxt;
    1008             :   }
    1009             : 
    1010             :   /* Update position of 'denom' in #denom_head DLL: sort by action time */
    1011             :   {
    1012             :     struct Denomination *before;
    1013             :     struct GNUNET_TIME_Absolute at;
    1014             : 
    1015           2 :     at = denomination_action_time (denom);
    1016           2 :     GNUNET_CONTAINER_DLL_remove (denom_head,
    1017             :                                  denom_tail,
    1018             :                                  denom);
    1019           2 :     before = NULL;
    1020           2 :     for (struct Denomination *pos = denom_head;
    1021             :          NULL != pos;
    1022           0 :          pos = pos->next)
    1023             :     {
    1024           0 :       if (GNUNET_TIME_absolute_cmp (denomination_action_time (pos), >=, at))
    1025           0 :         break;
    1026           0 :       before = pos;
    1027             :     }
    1028           2 :     GNUNET_CONTAINER_DLL_insert_after (denom_head,
    1029             :                                        denom_tail,
    1030             :                                        before,
    1031             :                                        denom);
    1032             :   }
    1033           2 :   return GNUNET_OK;
    1034             : }
    1035             : 
    1036             : 
    1037             : /**
    1038             :  * Task run periodically to expire keys and/or generate fresh ones.
    1039             :  *
    1040             :  * @param cls NULL
    1041             :  */
    1042             : static void
    1043           1 : update_denominations (void *cls)
    1044             : {
    1045             :   struct Denomination *denom;
    1046             :   struct GNUNET_TIME_Absolute now;
    1047             :   struct GNUNET_TIME_Timestamp t;
    1048           1 :   bool wake = false;
    1049             : 
    1050             :   (void) cls;
    1051           1 :   keygen_task = NULL;
    1052           1 :   now = GNUNET_TIME_absolute_get ();
    1053           1 :   t = GNUNET_TIME_absolute_to_timestamp (now);
    1054           1 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1055             :               "Updating denominations ...\n");
    1056           1 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    1057             :   do {
    1058           1 :     denom = denom_head;
    1059           1 :     if (GNUNET_OK !=
    1060           1 :         update_keys (denom,
    1061             :                      t,
    1062             :                      &wake))
    1063           0 :       return;
    1064           1 :   } while (denom != denom_head);
    1065           1 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    1066           1 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1067             :               "Updating denominations finished ...\n");
    1068           1 :   if (wake)
    1069           0 :     TES_wake_clients ();
    1070           1 :   keygen_task = GNUNET_SCHEDULER_add_at (denomination_action_time (denom),
    1071             :                                          &update_denominations,
    1072             :                                          NULL);
    1073             : }
    1074             : 
    1075             : 
    1076             : /**
    1077             :  * Parse private key of denomination @a denom in @a buf.
    1078             :  *
    1079             :  * @param[out] denom denomination of the key
    1080             :  * @param filename name of the file we are parsing, for logging
    1081             :  * @param priv key material
    1082             :  */
    1083             : static void
    1084           0 : parse_key (struct Denomination *denom,
    1085             :            const char *filename,
    1086             :            const struct GNUNET_CRYPTO_CsPrivateKey *priv)
    1087             : {
    1088             :   char *anchor_s;
    1089             :   char dummy;
    1090             :   unsigned long long anchor_ll;
    1091             :   struct GNUNET_TIME_Timestamp anchor;
    1092             : 
    1093           0 :   anchor_s = strrchr (filename,
    1094             :                       '/');
    1095           0 :   if (NULL == anchor_s)
    1096             :   {
    1097             :     /* File in a directory without '/' in the name, this makes no sense. */
    1098           0 :     GNUNET_break (0);
    1099           0 :     return;
    1100             :   }
    1101           0 :   anchor_s++;
    1102           0 :   if (1 != sscanf (anchor_s,
    1103             :                    "%llu%c",
    1104             :                    &anchor_ll,
    1105             :                    &dummy))
    1106             :   {
    1107             :     /* Filenames in KEYDIR must ONLY be the anchor time in seconds! */
    1108           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1109             :                 "Filename `%s' invalid for key file, skipping\n",
    1110             :                 filename);
    1111           0 :     return;
    1112             :   }
    1113             :   anchor.abs_time.abs_value_us
    1114           0 :     = anchor_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
    1115           0 :   if (anchor_ll != anchor.abs_time.abs_value_us
    1116           0 :       / GNUNET_TIME_UNIT_SECONDS.rel_value_us)
    1117             :   {
    1118             :     /* Integer overflow. Bad, invalid filename. */
    1119           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1120             :                 "Filename `%s' invalid for key file, skipping\n",
    1121             :                 filename);
    1122           0 :     return;
    1123             :   }
    1124             :   {
    1125             :     struct GNUNET_CRYPTO_CsPublicKey pub;
    1126             :     struct DenominationKey *dk;
    1127             :     struct DenominationKey *before;
    1128             : 
    1129           0 :     GNUNET_CRYPTO_cs_private_key_get_public (priv,
    1130             :                                              &pub);
    1131           0 :     dk = GNUNET_new (struct DenominationKey);
    1132           0 :     dk->denom_priv = *priv;
    1133           0 :     dk->denom = denom;
    1134           0 :     dk->anchor = anchor;
    1135           0 :     dk->filename = GNUNET_strdup (filename);
    1136           0 :     TALER_cs_pub_hash (&pub,
    1137             :                        &dk->h_cs);
    1138           0 :     dk->denom_pub = pub;
    1139           0 :     generate_response (dk);
    1140           0 :     if (GNUNET_OK !=
    1141           0 :         GNUNET_CONTAINER_multihashmap_put (
    1142             :           keys,
    1143           0 :           &dk->h_cs.hash,
    1144             :           dk,
    1145             :           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
    1146             :     {
    1147           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1148             :                   "Duplicate private key %s detected in file `%s'. Skipping.\n",
    1149             :                   GNUNET_h2s (&dk->h_cs.hash),
    1150             :                   filename);
    1151           0 :       GNUNET_free (dk->an);
    1152           0 :       GNUNET_free (dk);
    1153           0 :       return;
    1154             :     }
    1155           0 :     before = NULL;
    1156           0 :     for (struct DenominationKey *pos = denom->keys_head;
    1157             :          NULL != pos;
    1158           0 :          pos = pos->next)
    1159             :     {
    1160           0 :       if (GNUNET_TIME_timestamp_cmp (pos->anchor, >, anchor))
    1161           0 :         break;
    1162           0 :       before = pos;
    1163             :     }
    1164           0 :     GNUNET_CONTAINER_DLL_insert_after (denom->keys_head,
    1165             :                                        denom->keys_tail,
    1166             :                                        before,
    1167             :                                        dk);
    1168           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1169             :                 "Imported key %s from `%s'\n",
    1170             :                 GNUNET_h2s (&dk->h_cs.hash),
    1171             :                 filename);
    1172             :   }
    1173             : }
    1174             : 
    1175             : 
    1176             : /**
    1177             :  * Import a private key from @a filename for the denomination
    1178             :  * given in @a cls.
    1179             :  *
    1180             :  * @param[in,out] cls a `struct Denomiantion`
    1181             :  * @param filename name of a file in the directory
    1182             :  * @return #GNUNET_OK (always, continue to iterate)
    1183             :  */
    1184             : static enum GNUNET_GenericReturnValue
    1185           0 : import_key (void *cls,
    1186             :             const char *filename)
    1187             : {
    1188           0 :   struct Denomination *denom = cls;
    1189             :   struct GNUNET_DISK_FileHandle *fh;
    1190             :   struct GNUNET_DISK_MapHandle *map;
    1191             :   void *ptr;
    1192             :   int fd;
    1193             :   struct stat sbuf;
    1194             : 
    1195             :   {
    1196             :     struct stat lsbuf;
    1197             : 
    1198           0 :     if (0 != lstat (filename,
    1199             :                     &lsbuf))
    1200             :     {
    1201           0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    1202             :                                 "lstat",
    1203             :                                 filename);
    1204           0 :       return GNUNET_OK;
    1205             :     }
    1206           0 :     if (! S_ISREG (lsbuf.st_mode))
    1207             :     {
    1208           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1209             :                   "File `%s' is not a regular file, which is not allowed for private keys!\n",
    1210             :                   filename);
    1211           0 :       return GNUNET_OK;
    1212             :     }
    1213             :   }
    1214             : 
    1215           0 :   fd = open (filename,
    1216             :              O_RDONLY | O_CLOEXEC);
    1217           0 :   if (-1 == fd)
    1218             :   {
    1219           0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    1220             :                               "open",
    1221             :                               filename);
    1222           0 :     return GNUNET_OK;
    1223             :   }
    1224           0 :   if (0 != fstat (fd,
    1225             :                   &sbuf))
    1226             :   {
    1227           0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    1228             :                               "stat",
    1229             :                               filename);
    1230           0 :     GNUNET_break (0 == close (fd));
    1231           0 :     return GNUNET_OK;
    1232             :   }
    1233           0 :   if (! S_ISREG (sbuf.st_mode))
    1234             :   {
    1235           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1236             :                 "File `%s' is not a regular file, which is not allowed for private keys!\n",
    1237             :                 filename);
    1238           0 :     GNUNET_break (0 == close (fd));
    1239           0 :     return GNUNET_OK;
    1240             :   }
    1241           0 :   if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO)))
    1242             :   {
    1243             :     /* permission are NOT tight, try to patch them up! */
    1244           0 :     if (0 !=
    1245           0 :         fchmod (fd,
    1246             :                 S_IRUSR))
    1247             :     {
    1248           0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    1249             :                                 "fchmod",
    1250             :                                 filename);
    1251             :       /* refuse to use key if file has wrong permissions */
    1252           0 :       GNUNET_break (0 == close (fd));
    1253           0 :       return GNUNET_OK;
    1254             :     }
    1255             :   }
    1256           0 :   fh = GNUNET_DISK_get_handle_from_int_fd (fd);
    1257           0 :   if (NULL == fh)
    1258             :   {
    1259           0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    1260             :                               "open",
    1261             :                               filename);
    1262           0 :     GNUNET_break (0 == close (fd));
    1263           0 :     return GNUNET_OK;
    1264             :   }
    1265           0 :   if (sbuf.st_size != sizeof(struct GNUNET_CRYPTO_CsPrivateKey))
    1266             :   {
    1267           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1268             :                 "File `%s' too big to be a private key\n",
    1269             :                 filename);
    1270           0 :     GNUNET_DISK_file_close (fh);
    1271           0 :     return GNUNET_OK;
    1272             :   }
    1273           0 :   ptr = GNUNET_DISK_file_map (fh,
    1274             :                               &map,
    1275             :                               GNUNET_DISK_MAP_TYPE_READ,
    1276           0 :                               (size_t) sbuf.st_size);
    1277           0 :   if (NULL == ptr)
    1278             :   {
    1279           0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    1280             :                               "mmap",
    1281             :                               filename);
    1282           0 :     GNUNET_DISK_file_close (fh);
    1283           0 :     return GNUNET_OK;
    1284             :   }
    1285           0 :   parse_key (denom,
    1286             :              filename,
    1287             :              (const struct GNUNET_CRYPTO_CsPrivateKey *) ptr);
    1288           0 :   GNUNET_DISK_file_unmap (map);
    1289           0 :   GNUNET_DISK_file_close (fh);
    1290           0 :   return GNUNET_OK;
    1291             : }
    1292             : 
    1293             : 
    1294             : /**
    1295             :  * Parse configuration for denomination type parameters.  Also determines
    1296             :  * our anchor by looking at the existing denominations of the same type.
    1297             :  *
    1298             :  * @param cfg configuration to use
    1299             :  * @param ct section in the configuration file giving the denomination type parameters
    1300             :  * @param[out] denom set to the denomination parameters from the configuration
    1301             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the configuration is invalid
    1302             :  */
    1303             : static enum GNUNET_GenericReturnValue
    1304           1 : parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
    1305             :                         const char *ct,
    1306             :                         struct Denomination *denom)
    1307             : {
    1308           1 :   if (GNUNET_OK !=
    1309           1 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    1310             :                                            ct,
    1311             :                                            "DURATION_WITHDRAW",
    1312             :                                            &denom->duration_withdraw))
    1313             :   {
    1314           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1315             :                                ct,
    1316             :                                "DURATION_WITHDRAW");
    1317           0 :     return GNUNET_SYSERR;
    1318             :   }
    1319           1 :   if (GNUNET_TIME_relative_cmp (overlap_duration,
    1320             :                                 >=,
    1321             :                                 denom->duration_withdraw))
    1322             :   {
    1323           0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    1324             :                                "taler-exchange-secmod-cs",
    1325             :                                "OVERLAP_DURATION",
    1326             :                                "Value given must be smaller than value for DURATION_WITHDRAW!");
    1327           0 :     return GNUNET_SYSERR;
    1328             :   }
    1329           1 :   denom->section = GNUNET_strdup (ct);
    1330           1 :   return GNUNET_OK;
    1331             : }
    1332             : 
    1333             : 
    1334             : /**
    1335             :  * Closure for #load_denominations.
    1336             :  */
    1337             : struct LoadContext
    1338             : {
    1339             : 
    1340             :   /**
    1341             :    * Configuration to use.
    1342             :    */
    1343             :   const struct GNUNET_CONFIGURATION_Handle *cfg;
    1344             : 
    1345             :   /**
    1346             :    * Current time to use.
    1347             :    */
    1348             :   struct GNUNET_TIME_Timestamp t;
    1349             : 
    1350             :   /**
    1351             :    * Status, to be set to #GNUNET_SYSERR on failure
    1352             :    */
    1353             :   enum GNUNET_GenericReturnValue ret;
    1354             : };
    1355             : 
    1356             : 
    1357             : /**
    1358             :  * Generate new denomination signing keys for the denomination type of the given @a
    1359             :  * denomination_alias.
    1360             :  *
    1361             :  * @param cls a `struct LoadContext`, with 'ret' to be set to #GNUNET_SYSERR on failure
    1362             :  * @param denomination_alias name of the denomination's section in the configuration
    1363             :  */
    1364             : static void
    1365          22 : load_denominations (void *cls,
    1366             :                     const char *denomination_alias)
    1367             : {
    1368          22 :   struct LoadContext *ctx = cls;
    1369             :   struct Denomination *denom;
    1370          22 :   bool wake = true;
    1371             :   char *cipher;
    1372             : 
    1373          22 :   if ( (0 != strncasecmp (denomination_alias,
    1374             :                           "coin_",
    1375          21 :                           strlen ("coin_"))) &&
    1376          21 :        (0 != strncasecmp (denomination_alias,
    1377             :                           "coin-",
    1378             :                           strlen ("coin-"))) )
    1379          21 :     return; /* not a denomination type definition */
    1380           1 :   if (GNUNET_OK !=
    1381           1 :       GNUNET_CONFIGURATION_get_value_string (ctx->cfg,
    1382             :                                              denomination_alias,
    1383             :                                              "CIPHER",
    1384             :                                              &cipher))
    1385             :   {
    1386           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1387             :                                denomination_alias,
    1388             :                                "CIPHER");
    1389           0 :     return;
    1390             :   }
    1391           1 :   if (0 != strcmp (cipher, "CS"))
    1392             :   {
    1393           0 :     GNUNET_free (cipher);
    1394           0 :     return; /* Ignore denominations of other types than CS*/
    1395             :   }
    1396           1 :   GNUNET_free (cipher);
    1397             : 
    1398           1 :   denom = GNUNET_new (struct Denomination);
    1399           1 :   if (GNUNET_OK !=
    1400           1 :       parse_denomination_cfg (ctx->cfg,
    1401             :                               denomination_alias,
    1402             :                               denom))
    1403             :   {
    1404           0 :     ctx->ret = GNUNET_SYSERR;
    1405           0 :     GNUNET_free (denom);
    1406           0 :     return;
    1407             :   }
    1408           1 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1409             :               "Loading keys for denomination %s\n",
    1410             :               denom->section);
    1411             :   {
    1412             :     char *dname;
    1413             : 
    1414           1 :     GNUNET_asprintf (&dname,
    1415             :                      "%s/%s",
    1416             :                      keydir,
    1417             :                      denom->section);
    1418           1 :     GNUNET_break (GNUNET_OK ==
    1419             :                   GNUNET_DISK_directory_create (dname));
    1420           1 :     GNUNET_DISK_directory_scan (dname,
    1421             :                                 &import_key,
    1422             :                                 denom);
    1423           1 :     GNUNET_free (dname);
    1424             :   }
    1425           1 :   GNUNET_CONTAINER_DLL_insert (denom_head,
    1426             :                                denom_tail,
    1427             :                                denom);
    1428           1 :   update_keys (denom,
    1429             :                ctx->t,
    1430             :                &wake);
    1431             : }
    1432             : 
    1433             : 
    1434             : /**
    1435             :  * Load the various duration values from @a cfg
    1436             :  *
    1437             :  * @param cfg configuration to use
    1438             :  * @return #GNUNET_OK on success
    1439             :  */
    1440             : static enum GNUNET_GenericReturnValue
    1441           1 : load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg)
    1442             : {
    1443           1 :   if (GNUNET_OK !=
    1444           1 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    1445             :                                            "taler-exchange-secmod-cs",
    1446             :                                            "OVERLAP_DURATION",
    1447             :                                            &overlap_duration))
    1448             :   {
    1449           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1450             :                                "taler-exchange-secmod-cs",
    1451             :                                "OVERLAP_DURATION");
    1452           0 :     return GNUNET_SYSERR;
    1453             :   }
    1454           1 :   if (GNUNET_OK !=
    1455           1 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    1456             :                                            "taler-exchange-secmod-cs",
    1457             :                                            "LOOKAHEAD_SIGN",
    1458             :                                            &lookahead_sign))
    1459             :   {
    1460           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1461             :                                "taler-exchange-secmod-cs",
    1462             :                                "LOOKAHEAD_SIGN");
    1463           0 :     return GNUNET_SYSERR;
    1464             :   }
    1465           1 :   return GNUNET_OK;
    1466             : }
    1467             : 
    1468             : 
    1469             : /**
    1470             :  * Function run on shutdown. Stops the various jobs (nicely).
    1471             :  *
    1472             :  * @param cls NULL
    1473             :  */
    1474             : static void
    1475           1 : do_shutdown (void *cls)
    1476             : {
    1477             :   (void) cls;
    1478           1 :   TES_listen_stop ();
    1479           1 :   if (NULL != keygen_task)
    1480             :   {
    1481           1 :     GNUNET_SCHEDULER_cancel (keygen_task);
    1482           1 :     keygen_task = NULL;
    1483             :   }
    1484           1 : }
    1485             : 
    1486             : 
    1487             : /**
    1488             :  * Main function that will be run under the GNUnet scheduler.
    1489             :  *
    1490             :  * @param cls closure
    1491             :  * @param args remaining command-line arguments
    1492             :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    1493             :  * @param cfg configuration
    1494             :  */
    1495             : static void
    1496           1 : run (void *cls,
    1497             :      char *const *args,
    1498             :      const char *cfgfile,
    1499             :      const struct GNUNET_CONFIGURATION_Handle *cfg)
    1500             : {
    1501             :   static struct TES_Callbacks cb = {
    1502             :     .dispatch = &cs_work_dispatch,
    1503             :     .updater = &cs_update_client_keys,
    1504             :     .init = &cs_client_init
    1505             :   };
    1506             : 
    1507             :   (void) cls;
    1508             :   (void) args;
    1509             :   (void) cfgfile;
    1510           1 :   if (GNUNET_TIME_timestamp_cmp (now, !=, now_tmp))
    1511             :   {
    1512             :     /* The user gave "--now", use it! */
    1513           0 :     now = now_tmp;
    1514             :   }
    1515             :   else
    1516             :   {
    1517             :     /* get current time again, we may be timetraveling! */
    1518           1 :     now = GNUNET_TIME_timestamp_get ();
    1519             :   }
    1520           1 :   if (GNUNET_OK !=
    1521           1 :       GNUNET_CONFIGURATION_get_value_filename (cfg,
    1522             :                                                "taler-exchange-secmod-cs",
    1523             :                                                "KEY_DIR",
    1524             :                                                &keydir))
    1525             :   {
    1526           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1527             :                                "taler-exchange-secmod-cs",
    1528             :                                "KEY_DIR");
    1529           0 :     global_ret = EXIT_NOTCONFIGURED;
    1530           0 :     return;
    1531             :   }
    1532           1 :   if (GNUNET_OK !=
    1533           1 :       load_durations (cfg))
    1534             :   {
    1535           0 :     global_ret = EXIT_NOTCONFIGURED;
    1536           0 :     return;
    1537             :   }
    1538           1 :   global_ret = TES_listen_start (cfg,
    1539             :                                  "taler-exchange-secmod-cs",
    1540             :                                  &cb);
    1541           1 :   if (0 != global_ret)
    1542           0 :     return;
    1543           1 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    1544             :                                  NULL);
    1545             :   /* Load denominations */
    1546           1 :   keys = GNUNET_CONTAINER_multihashmap_create (65536,
    1547             :                                                GNUNET_YES);
    1548             :   {
    1549           1 :     struct LoadContext lc = {
    1550             :       .cfg = cfg,
    1551             :       .ret = GNUNET_OK,
    1552             :       .t = now
    1553             :     };
    1554             : 
    1555           1 :     GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    1556           1 :     GNUNET_CONFIGURATION_iterate_sections (cfg,
    1557             :                                            &load_denominations,
    1558             :                                            &lc);
    1559           1 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    1560           1 :     if (GNUNET_OK != lc.ret)
    1561             :     {
    1562           0 :       global_ret = EXIT_FAILURE;
    1563           0 :       GNUNET_SCHEDULER_shutdown ();
    1564           0 :       return;
    1565             :     }
    1566             :   }
    1567           1 :   if (NULL == denom_head)
    1568             :   {
    1569           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1570             :                 "No CS denominations configured\n");
    1571           0 :     TES_wake_clients ();
    1572           0 :     return;
    1573             :   }
    1574             :   /* start job to keep keys up-to-date; MUST be run before the #listen_task,
    1575             :      hence with priority. */
    1576           1 :   keygen_task = GNUNET_SCHEDULER_add_with_priority (
    1577             :     GNUNET_SCHEDULER_PRIORITY_URGENT,
    1578             :     &update_denominations,
    1579             :     NULL);
    1580             : }
    1581             : 
    1582             : 
    1583             : /**
    1584             :  * The entry point.
    1585             :  *
    1586             :  * @param argc number of arguments in @a argv
    1587             :  * @param argv command-line arguments
    1588             :  * @return 0 on normal termination
    1589             :  */
    1590             : int
    1591           1 : main (int argc,
    1592             :       char **argv)
    1593             : {
    1594           1 :   struct GNUNET_GETOPT_CommandLineOption options[] = {
    1595           1 :     GNUNET_GETOPT_option_timetravel ('T',
    1596             :                                      "timetravel"),
    1597           1 :     GNUNET_GETOPT_option_timestamp ('t',
    1598             :                                     "time",
    1599             :                                     "TIMESTAMP",
    1600             :                                     "pretend it is a different time for the update",
    1601             :                                     &now_tmp),
    1602             :     GNUNET_GETOPT_OPTION_END
    1603             :   };
    1604             :   enum GNUNET_GenericReturnValue ret;
    1605             : 
    1606             :   /* Restrict permissions for the key files that we create. */
    1607           1 :   (void) umask (S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH);
    1608             : 
    1609             :   /* force linker to link against libtalerutil; if we do
    1610             :    not do this, the linker may "optimize" libtalerutil
    1611             :    away and skip #TALER_OS_init(), which we do need */
    1612           1 :   TALER_OS_init ();
    1613           1 :   now_tmp = now = GNUNET_TIME_timestamp_get ();
    1614           1 :   ret = GNUNET_PROGRAM_run (argc, argv,
    1615             :                             "taler-exchange-secmod-cs",
    1616             :                             "Handle private CS key operations for a Taler exchange",
    1617             :                             options,
    1618             :                             &run,
    1619             :                             NULL);
    1620           1 :   if (GNUNET_NO == ret)
    1621           0 :     return EXIT_SUCCESS;
    1622           1 :   if (GNUNET_SYSERR == ret)
    1623           0 :     return EXIT_INVALIDARGUMENT;
    1624           1 :   return global_ret;
    1625             : }

Generated by: LCOV version 1.14