LCOV - code coverage report
Current view: top level - util - secmod_rsa.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 75.1 % 760 571
Test Date: 2026-09-11 18:55:36 Functions: 97.1 % 35 34

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2014-2026 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_rsa.c
      18              :  * @brief Standalone process to perform private key RSA 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/taler_util.h"
      36              : #include "secmod_rsa.h"
      37              : #include <gcrypt.h>
      38              : #include <pthread.h>
      39              : #include "taler/taler_error_codes.h"
      40              : #include "taler/taler_signatures.h"
      41              : #include "secmod_common.h"
      42              : #include <poll.h>
      43              : 
      44              : 
      45              : /**
      46              :  * Information we keep per denomination.
      47              :  */
      48              : struct Denomination;
      49              : 
      50              : 
      51              : /**
      52              :  * One particular denomination key.
      53              :  */
      54              : struct DenominationKey
      55              : {
      56              : 
      57              :   /**
      58              :    * Kept in a DLL of the respective denomination. Sorted by anchor time.
      59              :    */
      60              :   struct DenominationKey *next;
      61              : 
      62              :   /**
      63              :    * Kept in a DLL of the respective denomination. Sorted by anchor time.
      64              :    */
      65              :   struct DenominationKey *prev;
      66              : 
      67              :   /**
      68              :    * Denomination this key belongs to.
      69              :    */
      70              :   struct Denomination *denom;
      71              : 
      72              :   /**
      73              :    * Name of the file this key is stored under.
      74              :    */
      75              :   char *filename;
      76              : 
      77              :   /**
      78              :    * The private key of the denomination.
      79              :    */
      80              :   struct GNUNET_CRYPTO_RsaPrivateKey *denom_priv;
      81              : 
      82              :   /**
      83              :    * The public key of the denomination.
      84              :    */
      85              :   struct GNUNET_CRYPTO_RsaPublicKey *denom_pub;
      86              : 
      87              :   /**
      88              :    * Message to transmit to clients to introduce this public key.
      89              :    */
      90              :   struct TALER_CRYPTO_RsaKeyAvailableNotification *an;
      91              : 
      92              :   /**
      93              :    * Hash of this denomination's public key.
      94              :    */
      95              :   struct TALER_RsaPubHashP h_rsa;
      96              : 
      97              :   /**
      98              :    * Time at which this key is supposed to become valid.
      99              :    */
     100              :   struct GNUNET_TIME_Timestamp anchor_start;
     101              : 
     102              :   /**
     103              :    * Time at which this key is supposed to expire (exclusive).
     104              :    */
     105              :   struct GNUNET_TIME_Timestamp anchor_end;
     106              : 
     107              :   /**
     108              :    * Generation when this key was created or revoked.
     109              :    */
     110              :   uint64_t key_gen;
     111              : 
     112              :   /**
     113              :    * Reference counter. Counts the number of threads that are
     114              :    * using this key at this time.
     115              :    */
     116              :   unsigned int rc;
     117              : 
     118              :   /**
     119              :    * Flag set to true if this key has been purged and the memory
     120              :    * must be freed as soon as @e rc hits zero.
     121              :    */
     122              :   bool purge;
     123              : 
     124              : };
     125              : 
     126              : 
     127              : struct Denomination
     128              : {
     129              : 
     130              :   /**
     131              :    * Kept in a DLL.
     132              :    */
     133              :   struct Denomination *next;
     134              : 
     135              :   /**
     136              :    * Kept in a DLL.
     137              :    */
     138              :   struct Denomination *prev;
     139              : 
     140              :   /**
     141              :    * Head of DLL of actual keys of this denomination.
     142              :    */
     143              :   struct DenominationKey *keys_head;
     144              : 
     145              :   /**
     146              :    * Tail of DLL of actual keys of this denomination.
     147              :    */
     148              :   struct DenominationKey *keys_tail;
     149              : 
     150              :   /**
     151              :    * How long can coins be withdrawn (generated)?  Should be small
     152              :    * enough to limit how many coins will be signed into existence with
     153              :    * the same key, but large enough to still provide a reasonable
     154              :    * anonymity set.
     155              :    */
     156              :   struct GNUNET_TIME_Relative duration_withdraw;
     157              : 
     158              :   /**
     159              :    * Calendar interval the start of the validity period of our keys is
     160              :    * rounded down to (and the end of the validity period rounded up to).
     161              :    * #GNUNET_TIME_RI_NONE (the default) disables the rounding.  Donau sets
     162              :    * this to #GNUNET_TIME_RI_YEAR so that its keys are valid for exactly one
     163              :    * calendar year (starting January 1st UTC), even if the key was generated
     164              :    * in the middle of the year.
     165              :    */
     166              :   enum GNUNET_TIME_RounderInterval anchor_round;
     167              : 
     168              :   /**
     169              :    * What is the configuration section of this denomination type?  Also used
     170              :    * for the directory name where the denomination keys are stored.
     171              :    */
     172              :   char *section;
     173              : 
     174              :   /**
     175              :    * Length of (new) RSA keys (in bits).
     176              :    */
     177              :   uint32_t rsa_keysize;
     178              : };
     179              : 
     180              : 
     181              : /**
     182              :  * A semaphore.
     183              :  */
     184              : struct Semaphore
     185              : {
     186              :   /**
     187              :    * Mutex for the semaphore.
     188              :    */
     189              :   pthread_mutex_t mutex;
     190              : 
     191              :   /**
     192              :    * Condition variable for the semaphore.
     193              :    */
     194              :   pthread_cond_t cv;
     195              : 
     196              :   /**
     197              :    * Counter of the semaphore.
     198              :    */
     199              :   unsigned int ctr;
     200              : };
     201              : 
     202              : 
     203              : /**
     204              :  * Job in a batch sign request.
     205              :  */
     206              : struct BatchJob;
     207              : 
     208              : /**
     209              :  * Handle for a thread that does work in batch signing.
     210              :  */
     211              : struct Worker
     212              : {
     213              :   /**
     214              :    * Kept in a DLL.
     215              :    */
     216              :   struct Worker *prev;
     217              : 
     218              :   /**
     219              :    * Kept in a DLL.
     220              :    */
     221              :   struct Worker *next;
     222              : 
     223              :   /**
     224              :    * Job this worker should do next.
     225              :    */
     226              :   struct BatchJob *job;
     227              : 
     228              :   /**
     229              :    * Semaphore to signal the worker that a job is available.
     230              :    */
     231              :   struct Semaphore sem;
     232              : 
     233              :   /**
     234              :    * Handle for this thread.
     235              :    */
     236              :   pthread_t pt;
     237              : 
     238              :   /**
     239              :    * Set to true if the worker should terminate.
     240              :    */
     241              :   bool do_shutdown;
     242              : };
     243              : 
     244              : 
     245              : /**
     246              :  * Job in a batch sign request.
     247              :  */
     248              : struct BatchJob
     249              : {
     250              :   /**
     251              :    * Request we are working on.
     252              :    */
     253              :   const struct TALER_CRYPTO_SignRequest *sr;
     254              : 
     255              :   /**
     256              :    * Thread doing the work.
     257              :    */
     258              :   struct Worker *worker;
     259              : 
     260              :   /**
     261              :    * Result with the signature.
     262              :    */
     263              :   struct GNUNET_CRYPTO_RsaSignature *rsa_signature;
     264              : 
     265              :   /**
     266              :    * Semaphore to signal that the job is finished.
     267              :    */
     268              :   struct Semaphore sem;
     269              : 
     270              :   /**
     271              :    * Computation status.
     272              :    */
     273              :   enum TALER_ErrorCode ec;
     274              : 
     275              : };
     276              : 
     277              : 
     278              : /**
     279              :  * Head of DLL of workers ready for more work.
     280              :  */
     281              : static struct Worker *worker_head;
     282              : 
     283              : /**
     284              :  * Tail of DLL of workers ready for more work.
     285              :  */
     286              : static struct Worker *worker_tail;
     287              : 
     288              : /**
     289              :  * Lock for manipulating the worker DLL.
     290              :  */
     291              : static pthread_mutex_t worker_lock = PTHREAD_MUTEX_INITIALIZER;
     292              : 
     293              : /**
     294              :  * Total number of workers that were started.
     295              :  */
     296              : static unsigned int workers;
     297              : 
     298              : /**
     299              :  * Semaphore used to grab a worker.
     300              :  */
     301              : static struct Semaphore worker_sem;
     302              : 
     303              : /**
     304              :  * Command-line options for various TALER_SECMOD_XXX_run() functions.
     305              :  */
     306              : static struct TALER_SECMOD_Options *globals;
     307              : 
     308              : /**
     309              :  * Where do we store the keys?
     310              :  */
     311              : static char *keydir;
     312              : 
     313              : /**
     314              :  * How much should coin creation (@e duration_withdraw) duration overlap
     315              :  * with the next denomination?  Basically, the starting time of two
     316              :  * denominations is always @e duration_withdraw - #overlap_duration apart.
     317              :  */
     318              : static struct GNUNET_TIME_Relative overlap_duration;
     319              : 
     320              : /**
     321              :  * How long into the future do we pre-generate keys?
     322              :  */
     323              : static struct GNUNET_TIME_Relative lookahead_sign;
     324              : 
     325              : /**
     326              :  * All of our denominations, in a DLL. Sorted?
     327              :  */
     328              : static struct Denomination *denom_head;
     329              : 
     330              : /**
     331              :  * All of our denominations, in a DLL. Sorted?
     332              :  */
     333              : static struct Denomination *denom_tail;
     334              : 
     335              : /**
     336              :  * Map of hashes of public (RSA) keys to `struct DenominationKey *`
     337              :  * with the respective private keys.
     338              :  */
     339              : static struct GNUNET_CONTAINER_MultiHashMap *keys;
     340              : 
     341              : /**
     342              :  * Task run to generate new keys.
     343              :  */
     344              : static struct GNUNET_SCHEDULER_Task *keygen_task;
     345              : 
     346              : /**
     347              :  * Lock for the keys queue.
     348              :  */
     349              : static pthread_mutex_t keys_lock = PTHREAD_MUTEX_INITIALIZER;
     350              : 
     351              : /**
     352              :  * Current key generation.
     353              :  */
     354              : static uint64_t key_gen;
     355              : 
     356              : 
     357              : /**
     358              :  * Generate the announcement message for @a dk.
     359              :  *
     360              :  * @param[in,out] dk denomination key to generate the announcement for
     361              :  */
     362              : static void
     363          216 : generate_response (struct DenominationKey *dk)
     364              : {
     365          216 :   struct Denomination *denom = dk->denom;
     366          216 :   size_t nlen = strlen (denom->section) + 1;
     367              :   struct TALER_CRYPTO_RsaKeyAvailableNotification *an;
     368              :   size_t buf_len;
     369              :   void *buf;
     370              :   void *p;
     371              :   size_t tlen;
     372              :   struct GNUNET_TIME_Relative effective_duration;
     373              : 
     374          216 :   buf_len = GNUNET_CRYPTO_rsa_public_key_encode (dk->denom_pub,
     375              :                                                  &buf);
     376          216 :   GNUNET_assert (buf_len < UINT16_MAX);
     377          216 :   GNUNET_assert (nlen < UINT16_MAX);
     378          216 :   tlen = buf_len + nlen + sizeof (*an);
     379          216 :   GNUNET_assert (tlen < UINT16_MAX);
     380          216 :   an = GNUNET_malloc (tlen);
     381          216 :   an->header.size = htons ((uint16_t) tlen);
     382          216 :   an->header.type = htons (TALER_HELPER_RSA_MT_AVAIL);
     383          216 :   an->pub_size = htons ((uint16_t) buf_len);
     384          216 :   an->section_name_len = htons ((uint16_t) nlen);
     385          216 :   an->anchor_time = GNUNET_TIME_timestamp_hton (dk->anchor_start);
     386              :   /* Effective duration is based on denum->duration_withdraw + overlap,
     387              :      but we may have shifted the 'anchor_end' to align them, thus the
     388              :      only correct way to determine it is: */
     389          216 :   effective_duration = GNUNET_TIME_absolute_get_difference (
     390              :     dk->anchor_start.abs_time,
     391              :     dk->anchor_end.abs_time);
     392          216 :   an->duration_withdraw = GNUNET_TIME_relative_hton (effective_duration);
     393              : 
     394          216 :   TALER_exchange_secmod_rsa_sign (&dk->h_rsa,
     395          216 :                                   denom->section,
     396              :                                   dk->anchor_start,
     397              :                                   effective_duration,
     398              :                                   &TES_smpriv,
     399              :                                   &an->secm_sig);
     400          216 :   an->secm_pub = TES_smpub;
     401          216 :   p = (void *) &an[1];
     402          216 :   GNUNET_memcpy (p,
     403              :                  buf,
     404              :                  buf_len);
     405          216 :   GNUNET_free (buf);
     406          216 :   GNUNET_memcpy (p + buf_len,
     407              :                  denom->section,
     408              :                  nlen);
     409          216 :   dk->an = an;
     410          216 : }
     411              : 
     412              : 
     413              : /**
     414              :  * Do the actual signing work.
     415              :  *
     416              :  * @param h_rsa key to sign with
     417              :  * @param bm blinded message to sign
     418              :  * @param[out] rsa_signaturep set to the RSA signature
     419              :  * @return #TALER_EC_NONE on success
     420              :  */
     421              : static enum TALER_ErrorCode
     422         1404 : do_sign (const struct TALER_RsaPubHashP *h_rsa,
     423              :          const struct GNUNET_CRYPTO_RsaBlindedMessage *bm,
     424              :          struct GNUNET_CRYPTO_RsaSignature **rsa_signaturep)
     425              : {
     426              :   struct DenominationKey *dk;
     427              :   struct GNUNET_CRYPTO_RsaSignature *rsa_signature;
     428         1404 :   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
     429              : 
     430         1400 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     431         1423 :   dk = GNUNET_CONTAINER_multihashmap_get (keys,
     432              :                                           &h_rsa->hash);
     433         1423 :   if (NULL == dk)
     434              :   {
     435            3 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     436            3 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     437              :                 "Signing request failed, denomination key %s unknown\n",
     438              :                 GNUNET_h2s (&h_rsa->hash));
     439            3 :     return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
     440              :   }
     441         1420 :   if (dk->purge)
     442              :   {
     443              :     /* key was revoked, it must not be used for signing anymore */
     444            0 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     445            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     446              :                 "Signing request failed, denomination key %s was revoked\n",
     447              :                 GNUNET_h2s (&h_rsa->hash));
     448            0 :     return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED;
     449              :   }
     450         1420 :   if (GNUNET_TIME_absolute_is_future (dk->anchor_start.abs_time))
     451              :   {
     452              :     /* it is too early */
     453          268 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     454          266 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     455              :                 "Signing request failed, denomination key %s is not yet valid (%llu)\n",
     456              :                 GNUNET_h2s (&h_rsa->hash),
     457              :                 (unsigned long long) dk->anchor_start.abs_time.abs_value_us);
     458          264 :     return TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY;
     459              :   }
     460         1152 :   if (GNUNET_TIME_absolute_is_past (dk->anchor_end.abs_time))
     461              :   {
     462              :     /* it is too late; now, usually we should never get here
     463              :        as we delete upon expiration, so this is just conservative */
     464            0 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     465            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     466              :                 "Signing request failed, denomination key %s is expired (%llu)\n",
     467              :                 GNUNET_h2s (&h_rsa->hash),
     468              :                 (unsigned long long) dk->anchor_end.abs_time.abs_value_us);
     469              :     /* usually we delete upon expiratoin, hence same EC */
     470            0 :     return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
     471              :   }
     472              : 
     473         1152 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     474              :               "Received request to sign over %u bytes with key %s\n",
     475              :               (unsigned int) bm->blinded_msg_size,
     476              :               GNUNET_h2s (&h_rsa->hash));
     477         1152 :   GNUNET_assert (dk->rc < UINT_MAX);
     478         1152 :   dk->rc++;
     479         1152 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     480              :   rsa_signature
     481         1152 :     = GNUNET_CRYPTO_rsa_sign_blinded (dk->denom_priv,
     482              :                                       bm);
     483         1150 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     484         1152 :   GNUNET_assert (dk->rc > 0);
     485         1152 :   dk->rc--;
     486         1152 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     487         1151 :   if (NULL == rsa_signature)
     488              :   {
     489            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     490              :                 "Signing request failed, worker failed to produce signature\n");
     491            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     492              :   }
     493              : 
     494         1151 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     495              :               "Sending RSA signature after %s\n",
     496              :               GNUNET_TIME_relative2s (
     497              :                 GNUNET_TIME_absolute_get_duration (now),
     498              :                 GNUNET_YES));
     499         1151 :   *rsa_signaturep = rsa_signature;
     500         1151 :   return TALER_EC_NONE;
     501              : }
     502              : 
     503              : 
     504              : /**
     505              :  * Generate error response that signing failed.
     506              :  *
     507              :  * @param client client to send response to
     508              :  * @param ec error code to include
     509              :  * @return #GNUNET_OK on success
     510              :  */
     511              : static enum GNUNET_GenericReturnValue
     512          271 : fail_sign (struct TES_Client *client,
     513              :            enum TALER_ErrorCode ec)
     514              : {
     515          271 :   struct TALER_CRYPTO_SignFailure sf = {
     516          271 :     .header.size = htons (sizeof (sf)),
     517          271 :     .header.type = htons (TALER_HELPER_RSA_MT_RES_SIGN_FAILURE),
     518          271 :     .ec = htonl (ec)
     519              :   };
     520              : 
     521          271 :   return TES_transmit (client->csock,
     522              :                        &sf.header);
     523              : }
     524              : 
     525              : 
     526              : /**
     527              :  * Generate signature response.
     528              :  *
     529              :  * @param client client to send response to
     530              :  * @param[in] rsa_signature signature to send, freed by this function
     531              :  * @return #GNUNET_OK on success
     532              :  */
     533              : static enum GNUNET_GenericReturnValue
     534         1151 : send_signature (struct TES_Client *client,
     535              :                 struct GNUNET_CRYPTO_RsaSignature *rsa_signature)
     536              : {
     537              :   struct TALER_CRYPTO_SignResponse *sr;
     538              :   void *buf;
     539              :   size_t buf_size;
     540              :   size_t tsize;
     541              :   enum GNUNET_GenericReturnValue ret;
     542              : 
     543         1151 :   buf_size = GNUNET_CRYPTO_rsa_signature_encode (rsa_signature,
     544              :                                                  &buf);
     545         1152 :   GNUNET_CRYPTO_rsa_signature_free (rsa_signature);
     546         1152 :   tsize = sizeof (*sr) + buf_size;
     547         1152 :   GNUNET_assert (tsize < UINT16_MAX);
     548         1152 :   sr = GNUNET_malloc (tsize);
     549         1151 :   sr->header.size = htons (tsize);
     550         1151 :   sr->header.type = htons (TALER_HELPER_RSA_MT_RES_SIGNATURE);
     551         1151 :   GNUNET_memcpy (&sr[1],
     552              :                  buf,
     553              :                  buf_size);
     554         1151 :   GNUNET_free (buf);
     555         1152 :   ret = TES_transmit (client->csock,
     556         1152 :                       &sr->header);
     557         1151 :   GNUNET_free (sr);
     558         1151 :   return ret;
     559              : }
     560              : 
     561              : 
     562              : /**
     563              :  * Handle @a client request @a sr to create signature. Create the
     564              :  * signature using the respective key and return the result to
     565              :  * the client.
     566              :  *
     567              :  * @param client the client making the request
     568              :  * @param sr the request details
     569              :  * @return #GNUNET_OK on success
     570              :  */
     571              : static enum GNUNET_GenericReturnValue
     572            0 : handle_sign_request (struct TES_Client *client,
     573              :                      const struct TALER_CRYPTO_SignRequest *sr)
     574              : {
     575            0 :   struct GNUNET_CRYPTO_RsaBlindedMessage bm = {
     576            0 :     .blinded_msg = (void *) &sr[1],
     577            0 :     .blinded_msg_size = ntohs (sr->header.size) - sizeof (*sr)
     578              :   };
     579              :   struct GNUNET_CRYPTO_RsaSignature *rsa_signature;
     580              :   enum TALER_ErrorCode ec;
     581              : 
     582            0 :   ec = do_sign (&sr->h_rsa,
     583              :                 &bm,
     584              :                 &rsa_signature);
     585            0 :   if (TALER_EC_NONE != ec)
     586              :   {
     587            0 :     return fail_sign (client,
     588              :                       ec);
     589              :   }
     590            0 :   return send_signature (client,
     591              :                          rsa_signature);
     592              : }
     593              : 
     594              : 
     595              : /**
     596              :  * Initialize a semaphore @a sem with a value of @a val.
     597              :  *
     598              :  * @param[out] sem semaphore to initialize
     599              :  * @param val initial value of the semaphore
     600              :  */
     601              : static void
     602         1737 : sem_init (struct Semaphore *sem,
     603              :           unsigned int val)
     604              : {
     605         1737 :   GNUNET_assert (0 ==
     606              :                  pthread_mutex_init (&sem->mutex,
     607              :                                      NULL));
     608         1737 :   GNUNET_assert (0 ==
     609              :                  pthread_cond_init (&sem->cv,
     610              :                                     NULL));
     611         1736 :   sem->ctr = val;
     612         1736 : }
     613              : 
     614              : 
     615              : /**
     616              :  * Decrement semaphore, blocks until this is possible.
     617              :  *
     618              :  * @param[in,out] sem semaphore to decrement
     619              :  */
     620              : static void
     621         4847 : sem_down (struct Semaphore *sem)
     622              : {
     623         4847 :   GNUNET_assert (0 == pthread_mutex_lock (&sem->mutex));
     624         7616 :   while (0 == sem->ctr)
     625              :   {
     626         2773 :     pthread_cond_wait (&sem->cv,
     627              :                        &sem->mutex);
     628              :   }
     629         4843 :   sem->ctr--;
     630         4843 :   GNUNET_assert (0 == pthread_mutex_unlock (&sem->mutex));
     631         4825 : }
     632              : 
     633              : 
     634              : /**
     635              :  * Increment semaphore, blocks until this is possible.
     636              :  *
     637              :  * @param[in,out] sem semaphore to decrement
     638              :  */
     639              : static void
     640         4815 : sem_up (struct Semaphore *sem)
     641              : {
     642         4815 :   GNUNET_assert (0 == pthread_mutex_lock (&sem->mutex));
     643         4827 :   sem->ctr++;
     644         4827 :   pthread_cond_signal (&sem->cv);
     645         4816 :   GNUNET_assert (0 == pthread_mutex_unlock (&sem->mutex));
     646         4847 : }
     647              : 
     648              : 
     649              : /**
     650              :  * Release resources used by @a sem.
     651              :  *
     652              :  * @param[in] sem semaphore to release (except the memory itself)
     653              :  */
     654              : static void
     655         1737 : sem_done (struct Semaphore *sem)
     656              : {
     657         1737 :   GNUNET_break (0 == pthread_cond_destroy (&sem->cv));
     658         1737 :   GNUNET_break (0 == pthread_mutex_destroy (&sem->mutex));
     659         1736 : }
     660              : 
     661              : 
     662              : /**
     663              :  * Main logic of a worker thread. Grabs work, does it,
     664              :  * grabs more work.
     665              :  *
     666              :  * @param cls a `struct Worker *`
     667              :  * @returns cls
     668              :  */
     669              : static void *
     670          292 : worker (void *cls)
     671              : {
     672          292 :   struct Worker *w = cls;
     673              : 
     674              :   while (true)
     675              :   {
     676         1710 :     GNUNET_assert (0 == pthread_mutex_lock (&worker_lock));
     677         1715 :     GNUNET_CONTAINER_DLL_insert (worker_head,
     678              :                                  worker_tail,
     679              :                                  w);
     680         1715 :     GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock));
     681         1713 :     sem_up (&worker_sem);
     682         1714 :     sem_down (&w->sem);
     683         1701 :     if (w->do_shutdown)
     684          292 :       break;
     685              :     {
     686         1409 :       struct BatchJob *bj = w->job;
     687         1409 :       const struct TALER_CRYPTO_SignRequest *sr = bj->sr;
     688         1409 :       struct GNUNET_CRYPTO_RsaBlindedMessage bm = {
     689         1409 :         .blinded_msg = (void *) &sr[1],
     690         1409 :         .blinded_msg_size = ntohs (sr->header.size) - sizeof (*sr)
     691              :       };
     692              : 
     693         1409 :       bj->ec = do_sign (&sr->h_rsa,
     694              :                         &bm,
     695              :                         &bj->rsa_signature);
     696         1414 :       sem_up (&bj->sem);
     697         1418 :       w->job = NULL;
     698              :     }
     699              :   }
     700          292 :   return w;
     701              : }
     702              : 
     703              : 
     704              : /**
     705              :  * Start batch job @a bj to sign @a sr.
     706              :  *
     707              :  * @param sr signature request to answer
     708              :  * @param[out] bj job data structure
     709              :  */
     710              : static void
     711         1423 : start_job (const struct TALER_CRYPTO_SignRequest *sr,
     712              :            struct BatchJob *bj)
     713              : {
     714         1423 :   sem_init (&bj->sem,
     715              :             0);
     716         1423 :   bj->sr = sr;
     717         1423 :   sem_down (&worker_sem);
     718         1423 :   GNUNET_assert (0 == pthread_mutex_lock (&worker_lock));
     719         1423 :   bj->worker = worker_head;
     720         1423 :   GNUNET_CONTAINER_DLL_remove (worker_head,
     721              :                                worker_tail,
     722              :                                bj->worker);
     723         1423 :   GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock));
     724         1423 :   bj->worker->job = bj;
     725         1423 :   sem_up (&bj->worker->sem);
     726         1423 : }
     727              : 
     728              : 
     729              : /**
     730              :  * Finish a job @a bj for a @a client.
     731              :  *
     732              :  * @param client who made the request
     733              :  * @param[in,out] bj job to finish
     734              :  */
     735              : static void
     736         1423 : finish_job (struct TES_Client *client,
     737              :             struct BatchJob *bj)
     738              : {
     739         1423 :   sem_down (&bj->sem);
     740         1423 :   sem_done (&bj->sem);
     741         1422 :   if (TALER_EC_NONE != bj->ec)
     742              :   {
     743          271 :     fail_sign (client,
     744              :                bj->ec);
     745          271 :     return;
     746              :   }
     747         1151 :   GNUNET_assert (NULL != bj->rsa_signature);
     748         1151 :   send_signature (client,
     749              :                   bj->rsa_signature);
     750         1151 :   bj->rsa_signature = NULL; /* freed in send_signature */
     751              : }
     752              : 
     753              : 
     754              : /**
     755              :  * Handle @a client request @a sr to create a batch of signature. Creates the
     756              :  * signatures using the respective key and return the results to the client.
     757              :  *
     758              :  * @param client the client making the request
     759              :  * @param bsr the request details
     760              :  * @return #GNUNET_OK on success
     761              :  */
     762              : static enum GNUNET_GenericReturnValue
     763          974 : handle_batch_sign_request (struct TES_Client *client,
     764              :                            const struct TALER_CRYPTO_BatchSignRequest *bsr)
     765              : {
     766          974 :   uint32_t bs = ntohl (bsr->batch_size);
     767          974 :   uint16_t size = ntohs (bsr->header.size) - sizeof (*bsr);
     768          974 :   const void *off = (const void *) &bsr[1];
     769          974 :   unsigned int idx = 0;
     770          974 :   bool failure = false;
     771              : 
     772              :   /* an empty batch would be answered with no message at all,
     773              :      leaving the client waiting for a reply forever */
     774          974 :   if ( (0 == bs) ||
     775              :        (bs > TALER_MAX_COINS) )
     776              :   {
     777            0 :     GNUNET_break_op (0);
     778            0 :     return GNUNET_SYSERR;
     779              :   }
     780          974 :   {
     781          974 :     struct BatchJob jobs[bs];
     782              : 
     783         2397 :     while ( (idx < bs) &&
     784              :             (size > sizeof (struct TALER_CRYPTO_SignRequest)) )
     785              :     {
     786         1423 :       const struct TALER_CRYPTO_SignRequest *sr = off;
     787         1423 :       uint16_t s = ntohs (sr->header.size);
     788              : 
     789         1423 :       if ( (s > size) ||
     790              :            (s < sizeof (*sr)) )
     791              :       {
     792            0 :         failure = true;
     793            0 :         bs = idx;
     794            0 :         break;
     795              :       }
     796         1423 :       start_job (sr,
     797         1423 :                  &jobs[idx++]);
     798         1423 :       off += s;
     799         1423 :       size -= s;
     800              :     }
     801          974 :     GNUNET_break_op (0 == size);
     802          974 :     bs = GNUNET_MIN (bs,
     803              :                      idx);
     804         2396 :     for (unsigned int i = 0; i<bs; i++)
     805         1423 :       finish_job (client,
     806              :                   &jobs[i]);
     807              :   }
     808          973 :   if (failure)
     809              :   {
     810            0 :     struct TALER_CRYPTO_SignFailure sf = {
     811            0 :       .header.size = htons (sizeof (sf)),
     812            0 :       .header.type = htons (TALER_HELPER_RSA_MT_RES_BATCH_FAILURE),
     813            0 :       .ec = htonl (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE)
     814              :     };
     815              : 
     816            0 :     GNUNET_break (0);
     817            0 :     return TES_transmit (client->csock,
     818              :                          &sf.header);
     819              :   }
     820          973 :   return GNUNET_OK;
     821              : }
     822              : 
     823              : 
     824              : /**
     825              :  * Start worker thread for batch processing.
     826              :  *
     827              :  * @return #GNUNET_OK on success
     828              :  */
     829              : static enum GNUNET_GenericReturnValue
     830          292 : start_worker (void)
     831              : {
     832              :   struct Worker *w;
     833              : 
     834          292 :   w = GNUNET_new (struct Worker);
     835          292 :   sem_init (&w->sem,
     836              :             0);
     837          292 :   if (0 != pthread_create (&w->pt,
     838              :                            NULL,
     839              :                            &worker,
     840              :                            w))
     841              :   {
     842            0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     843              :                          "pthread_create");
     844            0 :     GNUNET_free (w);
     845            0 :     return GNUNET_SYSERR;
     846              :   }
     847          292 :   workers++;
     848          292 :   return GNUNET_OK;
     849              : }
     850              : 
     851              : 
     852              : /**
     853              :  * Stop all worker threads.
     854              :  */
     855              : static void
     856           22 : stop_workers (void)
     857              : {
     858          314 :   while (workers > 0)
     859              :   {
     860              :     struct Worker *w;
     861              :     void *result;
     862              : 
     863          292 :     sem_down (&worker_sem);
     864          292 :     GNUNET_assert (0 == pthread_mutex_lock (&worker_lock));
     865          292 :     w = worker_head;
     866          292 :     GNUNET_CONTAINER_DLL_remove (worker_head,
     867              :                                  worker_tail,
     868              :                                  w);
     869          292 :     GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock));
     870          292 :     w->do_shutdown = true;
     871          292 :     sem_up (&w->sem);
     872          292 :     pthread_join (w->pt,
     873              :                   &result);
     874          292 :     GNUNET_assert (result == w);
     875          292 :     sem_done (&w->sem);
     876          292 :     GNUNET_free (w);
     877          292 :     workers--;
     878              :   }
     879           22 : }
     880              : 
     881              : 
     882              : /**
     883              :  * Initialize key material for denomination key @a dk (also on disk).
     884              :  *
     885              :  * @param[in,out] dk denomination key to compute key material for
     886              :  * @param position where in the DLL will the @a dk go
     887              :  * @return #GNUNET_OK on success
     888              :  */
     889              : static enum GNUNET_GenericReturnValue
     890          207 : setup_key (struct DenominationKey *dk,
     891              :            struct DenominationKey *position)
     892              : {
     893          207 :   struct Denomination *denom = dk->denom;
     894              :   struct GNUNET_CRYPTO_RsaPrivateKey *priv;
     895              :   struct GNUNET_CRYPTO_RsaPublicKey *pub;
     896              :   size_t buf_size;
     897              :   void *buf;
     898              : 
     899          207 :   priv = GNUNET_CRYPTO_rsa_private_key_create (denom->rsa_keysize);
     900          207 :   if (NULL == priv)
     901              :   {
     902            0 :     GNUNET_break (0);
     903            0 :     GNUNET_SCHEDULER_shutdown ();
     904            0 :     globals->global_ret = EXIT_FAILURE;
     905            0 :     return GNUNET_SYSERR;
     906              :   }
     907          207 :   pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv);
     908          207 :   if (NULL == pub)
     909              :   {
     910            0 :     GNUNET_break (0);
     911            0 :     GNUNET_CRYPTO_rsa_private_key_free (priv);
     912            0 :     return GNUNET_SYSERR;
     913              :   }
     914          207 :   buf_size = GNUNET_CRYPTO_rsa_private_key_encode (priv,
     915              :                                                    &buf);
     916          207 :   GNUNET_CRYPTO_rsa_public_key_hash (pub,
     917              :                                      &dk->h_rsa.hash);
     918          207 :   GNUNET_asprintf (
     919              :     &dk->filename,
     920              :     "%s/%s/%llu-%llu",
     921              :     keydir,
     922              :     denom->section,
     923          207 :     (unsigned long long) (dk->anchor_start.abs_time.abs_value_us
     924          207 :                           / GNUNET_TIME_UNIT_SECONDS.rel_value_us),
     925          207 :     (unsigned long long) (dk->anchor_end.abs_time.abs_value_us
     926          207 :                           / GNUNET_TIME_UNIT_SECONDS.rel_value_us));
     927          207 :   if (GNUNET_OK !=
     928          207 :       GNUNET_DISK_fn_write (dk->filename,
     929              :                             buf,
     930              :                             buf_size,
     931              :                             GNUNET_DISK_PERM_USER_READ))
     932              :   {
     933            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     934              :                               "write",
     935              :                               dk->filename);
     936            0 :     GNUNET_free (dk->filename);
     937            0 :     GNUNET_free (buf);
     938            0 :     GNUNET_CRYPTO_rsa_private_key_free (priv);
     939            0 :     GNUNET_CRYPTO_rsa_public_key_free (pub);
     940            0 :     return GNUNET_SYSERR;
     941              :   }
     942          207 :   GNUNET_free (buf);
     943          207 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     944              :               "Setup fresh private key %s at %s in `%s' (generation #%llu)\n",
     945              :               GNUNET_h2s (&dk->h_rsa.hash),
     946              :               GNUNET_TIME_timestamp2s (dk->anchor_start),
     947              :               dk->filename,
     948              :               (unsigned long long) key_gen);
     949          207 :   dk->denom_priv = priv;
     950          207 :   dk->denom_pub = pub;
     951          207 :   dk->key_gen = key_gen;
     952          207 :   generate_response (dk);
     953          207 :   if (GNUNET_OK !=
     954          207 :       GNUNET_CONTAINER_multihashmap_put (
     955              :         keys,
     956          207 :         &dk->h_rsa.hash,
     957              :         dk,
     958              :         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
     959              :   {
     960            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     961              :                 "Duplicate private key created! Terminating.\n");
     962            0 :     GNUNET_CRYPTO_rsa_private_key_free (dk->denom_priv);
     963            0 :     GNUNET_CRYPTO_rsa_public_key_free (dk->denom_pub);
     964            0 :     GNUNET_free (dk->filename);
     965            0 :     GNUNET_free (dk->an);
     966            0 :     GNUNET_free (dk);
     967            0 :     return GNUNET_SYSERR;
     968              :   }
     969          207 :   GNUNET_CONTAINER_DLL_insert_after (denom->keys_head,
     970              :                                      denom->keys_tail,
     971              :                                      position,
     972              :                                      dk);
     973          207 :   return GNUNET_OK;
     974              : }
     975              : 
     976              : 
     977              : /**
     978              :  * The withdraw period of a key @a dk has expired. Purge it.
     979              :  *
     980              :  * @param[in] dk expired denomination key to purge
     981              :  */
     982              : static void
     983            3 : purge_key (struct DenominationKey *dk)
     984              : {
     985            3 :   if (dk->purge)
     986            0 :     return;
     987            3 :   if (0 != unlink (dk->filename))
     988            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     989              :                               "unlink",
     990              :                               dk->filename);
     991            3 :   GNUNET_free (dk->filename);
     992            3 :   dk->purge = true;
     993            3 :   dk->key_gen = key_gen;
     994              : }
     995              : 
     996              : 
     997              : /**
     998              :  * A @a client informs us that a key has been revoked.
     999              :  * Check if the key is still in use, and if so replace (!)
    1000              :  * it with a fresh key.
    1001              :  *
    1002              :  * @param client the client making the request
    1003              :  * @param rr the revocation request
    1004              :  */
    1005              : static enum GNUNET_GenericReturnValue
    1006            3 : handle_revoke_request (struct TES_Client *client,
    1007              :                        const struct TALER_CRYPTO_RevokeRequest *rr)
    1008              : {
    1009              :   struct DenominationKey *dk;
    1010              :   struct DenominationKey *ndk;
    1011              :   struct Denomination *denom;
    1012              : 
    1013              :   (void) client;
    1014            3 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    1015            3 :   dk = GNUNET_CONTAINER_multihashmap_get (keys,
    1016              :                                           &rr->h_rsa.hash);
    1017            3 :   if (NULL == dk)
    1018              :   {
    1019            0 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    1020            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1021              :                 "Revocation request ignored, denomination key %s unknown\n",
    1022              :                 GNUNET_h2s (&rr->h_rsa.hash));
    1023            0 :     return GNUNET_OK;
    1024              :   }
    1025            3 :   if (dk->purge)
    1026              :   {
    1027            0 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    1028            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1029              :                 "Revocation request ignored, denomination key %s already revoked\n",
    1030              :                 GNUNET_h2s (&rr->h_rsa.hash));
    1031            0 :     return GNUNET_OK;
    1032              :   }
    1033              : 
    1034            3 :   key_gen++;
    1035            3 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1036              :               "Revoking key %s, bumping generation to %llu\n",
    1037              :               GNUNET_h2s (&rr->h_rsa.hash),
    1038              :               (unsigned long long) key_gen);
    1039            3 :   purge_key (dk);
    1040              : 
    1041              :   /* Setup replacement key */
    1042            3 :   denom = dk->denom;
    1043            3 :   ndk = GNUNET_new (struct DenominationKey);
    1044            3 :   ndk->denom = denom;
    1045            3 :   ndk->anchor_start = dk->anchor_start;
    1046            3 :   ndk->anchor_end = dk->anchor_end;
    1047            3 :   if (GNUNET_OK !=
    1048            3 :       setup_key (ndk,
    1049              :                  dk))
    1050              :   {
    1051            0 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    1052            0 :     GNUNET_break (0);
    1053            0 :     GNUNET_SCHEDULER_shutdown ();
    1054            0 :     globals->global_ret = EXIT_FAILURE;
    1055            0 :     return GNUNET_SYSERR;
    1056              :   }
    1057            3 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    1058            3 :   TES_wake_clients ();
    1059            3 :   return GNUNET_OK;
    1060              : }
    1061              : 
    1062              : 
    1063              : /**
    1064              :  * Handle @a hdr message received from @a client.
    1065              :  *
    1066              :  * @param client the client that received the message
    1067              :  * @param hdr message that was received
    1068              :  * @return #GNUNET_OK on success
    1069              :  */
    1070              : static enum GNUNET_GenericReturnValue
    1071          977 : rsa_work_dispatch (struct TES_Client *client,
    1072              :                    const struct GNUNET_MessageHeader *hdr)
    1073              : {
    1074          977 :   uint16_t msize = ntohs (hdr->size);
    1075              : 
    1076          977 :   switch (ntohs (hdr->type))
    1077              :   {
    1078            0 :   case TALER_HELPER_RSA_MT_REQ_SIGN:
    1079            0 :     if (msize <= sizeof (struct TALER_CRYPTO_SignRequest))
    1080              :     {
    1081            0 :       GNUNET_break_op (0);
    1082            0 :       return GNUNET_SYSERR;
    1083              :     }
    1084            0 :     return handle_sign_request (
    1085              :       client,
    1086              :       (const struct TALER_CRYPTO_SignRequest *) hdr);
    1087            3 :   case TALER_HELPER_RSA_MT_REQ_REVOKE:
    1088            3 :     if (msize != sizeof (struct TALER_CRYPTO_RevokeRequest))
    1089              :     {
    1090            0 :       GNUNET_break_op (0);
    1091            0 :       return GNUNET_SYSERR;
    1092              :     }
    1093            3 :     return handle_revoke_request (
    1094              :       client,
    1095              :       (const struct TALER_CRYPTO_RevokeRequest *) hdr);
    1096          974 :   case TALER_HELPER_RSA_MT_REQ_BATCH_SIGN:
    1097          974 :     if (msize <= sizeof (struct TALER_CRYPTO_BatchSignRequest))
    1098              :     {
    1099            0 :       GNUNET_break_op (0);
    1100            0 :       return GNUNET_SYSERR;
    1101              :     }
    1102          974 :     return handle_batch_sign_request (
    1103              :       client,
    1104              :       (const struct TALER_CRYPTO_BatchSignRequest *) hdr);
    1105            0 :   default:
    1106            0 :     GNUNET_break_op (0);
    1107            0 :     return GNUNET_SYSERR;
    1108              :   }
    1109              : }
    1110              : 
    1111              : 
    1112              : /**
    1113              :  * Send our initial key set to @a client together with the
    1114              :  * "sync" terminator.
    1115              :  *
    1116              :  * @param client the client to inform
    1117              :  * @return #GNUNET_OK on success
    1118              :  */
    1119              : static enum GNUNET_GenericReturnValue
    1120           30 : rsa_client_init (struct TES_Client *client)
    1121              : {
    1122           30 :   size_t obs = 0;
    1123              :   char *buf;
    1124              : 
    1125           30 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1126              :               "Initializing new client %p\n",
    1127              :               client);
    1128           30 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    1129           30 :   for (struct Denomination *denom = denom_head;
    1130          107 :        NULL != denom;
    1131           77 :        denom = denom->next)
    1132              :   {
    1133           77 :     for (struct DenominationKey *dk = denom->keys_head;
    1134          352 :          NULL != dk;
    1135          275 :          dk = dk->next)
    1136              :     {
    1137          275 :       GNUNET_assert (obs + ntohs (dk->an->header.size)
    1138              :                      > obs);
    1139          275 :       obs += ntohs (dk->an->header.size);
    1140              :     }
    1141              :   }
    1142           30 :   buf = GNUNET_malloc (obs);
    1143           30 :   obs = 0;
    1144           30 :   for (struct Denomination *denom = denom_head;
    1145          107 :        NULL != denom;
    1146           77 :        denom = denom->next)
    1147              :   {
    1148           77 :     for (struct DenominationKey *dk = denom->keys_head;
    1149          352 :          NULL != dk;
    1150          275 :          dk = dk->next)
    1151              :     {
    1152          275 :       GNUNET_memcpy (&buf[obs],
    1153              :                      dk->an,
    1154              :                      ntohs (dk->an->header.size));
    1155          275 :       GNUNET_assert (obs + ntohs (dk->an->header.size)
    1156              :                      > obs);
    1157          275 :       obs += ntohs (dk->an->header.size);
    1158              :     }
    1159              :   }
    1160           30 :   client->key_gen = key_gen;
    1161           30 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    1162           30 :   if (GNUNET_OK !=
    1163           30 :       TES_transmit_raw (client->csock,
    1164              :                         obs,
    1165              :                         buf))
    1166              :   {
    1167            0 :     GNUNET_free (buf);
    1168            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1169              :                 "Client %p must have disconnected\n",
    1170              :                 client);
    1171            0 :     return GNUNET_SYSERR;
    1172              :   }
    1173           30 :   GNUNET_free (buf);
    1174              :   {
    1175           30 :     struct GNUNET_MessageHeader synced = {
    1176           30 :       .type = htons (TALER_HELPER_RSA_SYNCED),
    1177           30 :       .size = htons (sizeof (synced))
    1178              :     };
    1179              : 
    1180           30 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1181              :                 "Sending RSA SYNCED message to %p\n",
    1182              :                 client);
    1183           30 :     if (GNUNET_OK !=
    1184           30 :         TES_transmit (client->csock,
    1185              :                       &synced))
    1186              :     {
    1187            0 :       GNUNET_break (0);
    1188            0 :       return GNUNET_SYSERR;
    1189              :     }
    1190              :   }
    1191           30 :   return GNUNET_OK;
    1192              : }
    1193              : 
    1194              : 
    1195              : /**
    1196              :  * Notify @a client about all changes to the keys since
    1197              :  * the last generation known to the @a client.
    1198              :  *
    1199              :  * @param client the client to notify
    1200              :  * @return #GNUNET_OK on success
    1201              :  */
    1202              : static enum GNUNET_GenericReturnValue
    1203           22 : rsa_update_client_keys (struct TES_Client *client)
    1204              : {
    1205           22 :   size_t obs = 0;
    1206              :   char *buf;
    1207              :   enum GNUNET_GenericReturnValue ret;
    1208              : 
    1209           22 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    1210           22 :   for (struct Denomination *denom = denom_head;
    1211           91 :        NULL != denom;
    1212           69 :        denom = denom->next)
    1213              :   {
    1214           69 :     for (struct DenominationKey *key = denom->keys_head;
    1215          295 :          NULL != key;
    1216          226 :          key = key->next)
    1217              :     {
    1218          226 :       if (key->key_gen <= client->key_gen)
    1219          218 :         continue;
    1220            8 :       if (key->purge)
    1221            3 :         obs += sizeof (struct TALER_CRYPTO_RsaKeyPurgeNotification);
    1222              :       else
    1223            5 :         obs += ntohs (key->an->header.size);
    1224              :     }
    1225              :   }
    1226           22 :   if (0 == obs)
    1227              :   {
    1228              :     /* nothing to do */
    1229           17 :     client->key_gen = key_gen;
    1230           17 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    1231           17 :     return GNUNET_OK;
    1232              :   }
    1233            5 :   buf = GNUNET_malloc (obs);
    1234            5 :   obs = 0;
    1235            5 :   for (struct Denomination *denom = denom_head;
    1236           10 :        NULL != denom;
    1237            5 :        denom = denom->next)
    1238              :   {
    1239            5 :     for (struct DenominationKey *key = denom->keys_head;
    1240           36 :          NULL != key;
    1241           31 :          key = key->next)
    1242              :     {
    1243           31 :       if (key->key_gen <= client->key_gen)
    1244           23 :         continue;
    1245            8 :       if (key->purge)
    1246              :       {
    1247            3 :         struct TALER_CRYPTO_RsaKeyPurgeNotification pn = {
    1248            3 :           .header.type = htons (TALER_HELPER_RSA_MT_PURGE),
    1249            3 :           .header.size = htons (sizeof (pn)),
    1250              :           .h_rsa = key->h_rsa
    1251              :         };
    1252              : 
    1253            3 :         GNUNET_memcpy (&buf[obs],
    1254              :                        &pn,
    1255              :                        sizeof (pn));
    1256            3 :         GNUNET_assert (obs + sizeof (pn)
    1257              :                        > obs);
    1258            3 :         obs += sizeof (pn);
    1259              :       }
    1260              :       else
    1261              :       {
    1262            5 :         GNUNET_memcpy (&buf[obs],
    1263              :                        key->an,
    1264              :                        ntohs (key->an->header.size));
    1265            5 :         GNUNET_assert (obs + ntohs (key->an->header.size)
    1266              :                        > obs);
    1267            5 :         obs += ntohs (key->an->header.size);
    1268              :       }
    1269              :     }
    1270              :   }
    1271            5 :   client->key_gen = key_gen;
    1272            5 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    1273            5 :   ret = TES_transmit_raw (client->csock,
    1274              :                           obs,
    1275              :                           buf);
    1276            5 :   GNUNET_free (buf);
    1277            5 :   return ret;
    1278              : }
    1279              : 
    1280              : 
    1281              : /**
    1282              :  * Create a new denomination key (we do not have enough).
    1283              :  *
    1284              :  * @param[in,out] denom denomination key to create
    1285              :  * @param anchor_start when to start key signing validity
    1286              :  * @param anchor_end when to end key signing validity
    1287              :  * @return #GNUNET_OK on success
    1288              :  */
    1289              : static enum GNUNET_GenericReturnValue
    1290          204 : create_key (struct Denomination *denom,
    1291              :             struct GNUNET_TIME_Timestamp anchor_start,
    1292              :             struct GNUNET_TIME_Timestamp anchor_end)
    1293              : {
    1294              :   struct DenominationKey *dk;
    1295              : 
    1296          204 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1297              :               "Creating new key for `%s' with start date %s\n",
    1298              :               denom->section,
    1299              :               GNUNET_TIME_timestamp2s (anchor_start));
    1300          204 :   dk = GNUNET_new (struct DenominationKey);
    1301          204 :   dk->denom = denom;
    1302          204 :   dk->anchor_start = anchor_start;
    1303          204 :   dk->anchor_end = anchor_end;
    1304          204 :   if (GNUNET_OK !=
    1305          204 :       setup_key (dk,
    1306              :                  denom->keys_tail))
    1307              :   {
    1308            0 :     GNUNET_break (0);
    1309            0 :     GNUNET_free (dk);
    1310            0 :     GNUNET_SCHEDULER_shutdown ();
    1311            0 :     globals->global_ret = EXIT_FAILURE;
    1312            0 :     return GNUNET_SYSERR;
    1313              :   }
    1314          204 :   return GNUNET_OK;
    1315              : }
    1316              : 
    1317              : 
    1318              : /**
    1319              :  * Obtain the maximum withdraw duration of all denominations.
    1320              :  *
    1321              :  * Must only be called while the #keys_lock is held.
    1322              :  *
    1323              :  * @return maximum withdraw duration, zero if there are no denominations
    1324              :  */
    1325              : static struct GNUNET_TIME_Relative
    1326           56 : get_maximum_duration (void)
    1327              : {
    1328           56 :   struct GNUNET_TIME_Relative ret
    1329              :     = GNUNET_TIME_UNIT_ZERO;
    1330              : 
    1331           56 :   for (struct Denomination *denom = denom_head;
    1332          267 :        NULL != denom;
    1333          211 :        denom = denom->next)
    1334              :   {
    1335          211 :     ret = GNUNET_TIME_relative_max (ret,
    1336              :                                     denom->duration_withdraw);
    1337              :   }
    1338           56 :   return ret;
    1339              : }
    1340              : 
    1341              : 
    1342              : /**
    1343              :  * At what time do we need to next create keys if we just did?
    1344              :  *
    1345              :  * @return time when to next create keys if we just finished key generation
    1346              :  */
    1347              : static struct GNUNET_TIME_Absolute
    1348           17 : action_time (void)
    1349              : {
    1350           17 :   struct GNUNET_TIME_Relative md = get_maximum_duration ();
    1351           17 :   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
    1352              :   uint64_t mod;
    1353              : 
    1354           17 :   if (GNUNET_TIME_relative_is_zero (md))
    1355            0 :     return GNUNET_TIME_UNIT_FOREVER_ABS;
    1356           17 :   mod = now.abs_value_us % md.rel_value_us;
    1357           17 :   now.abs_value_us -= mod;
    1358           17 :   return GNUNET_TIME_absolute_add (now,
    1359              :                                    md);
    1360              : }
    1361              : 
    1362              : 
    1363              : /**
    1364              :  * Remove all denomination keys of @a denom that have expired.
    1365              :  *
    1366              :  * @param[in,out] denom denomination family to remove keys for
    1367              :  */
    1368              : static void
    1369          280 : remove_expired_denomination_keys (struct Denomination *denom)
    1370              : {
    1371          496 :   while ( (NULL != denom->keys_head) &&
    1372          215 :           GNUNET_TIME_absolute_is_past (
    1373          215 :             denom->keys_head->anchor_end.abs_time))
    1374              :   {
    1375            1 :     struct DenominationKey *key = denom->keys_head;
    1376            1 :     struct DenominationKey *nxt = key->next;
    1377              : 
    1378            1 :     if (0 != key->rc)
    1379            0 :       break; /* later */
    1380            1 :     GNUNET_CONTAINER_DLL_remove (denom->keys_head,
    1381              :                                  denom->keys_tail,
    1382              :                                  key);
    1383            1 :     GNUNET_assert (GNUNET_OK ==
    1384              :                    GNUNET_CONTAINER_multihashmap_remove (
    1385              :                      keys,
    1386              :                      &key->h_rsa.hash,
    1387              :                      key));
    1388            2 :     if ( (! key->purge) &&
    1389            1 :          (0 != unlink (key->filename)) )
    1390            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    1391              :                                 "unlink",
    1392              :                                 key->filename);
    1393            1 :     GNUNET_free (key->filename);
    1394            1 :     GNUNET_CRYPTO_rsa_private_key_free (key->denom_priv);
    1395            1 :     GNUNET_CRYPTO_rsa_public_key_free (key->denom_pub);
    1396            1 :     GNUNET_free (key->an);
    1397            1 :     GNUNET_free (key);
    1398            1 :     key = nxt;
    1399              :   }
    1400          280 : }
    1401              : 
    1402              : 
    1403              : /**
    1404              :  * Obtain the end anchor to use at this point. Uses the
    1405              :  * #lookahead_sign and then rounds it up by the maximum
    1406              :  * duration of any denomination to arrive at a globally
    1407              :  * valid end-date.
    1408              :  *
    1409              :  * Must only be called while the #keys_lock is held.
    1410              :  *
    1411              :  * @return end anchor
    1412              :  */
    1413              : static struct GNUNET_TIME_Timestamp
    1414           39 : get_anchor_end (void)
    1415              : {
    1416           39 :   struct GNUNET_TIME_Relative md = get_maximum_duration ();
    1417              :   struct GNUNET_TIME_Absolute end
    1418           39 :     = GNUNET_TIME_relative_to_absolute (lookahead_sign);
    1419              :   uint64_t mod;
    1420              : 
    1421           39 :   if (GNUNET_TIME_relative_is_zero (md))
    1422            7 :     return GNUNET_TIME_UNIT_ZERO_TS;
    1423              :   /* Round up 'end' to a multiple of 'md' */
    1424           32 :   mod = end.abs_value_us % md.rel_value_us;
    1425           32 :   end.abs_value_us -= mod;
    1426           32 :   return GNUNET_TIME_absolute_to_timestamp (
    1427              :     GNUNET_TIME_absolute_add (end,
    1428              :                               md));
    1429              : }
    1430              : 
    1431              : 
    1432              : /**
    1433              :  * Create all denomination keys that are required for our
    1434              :  * desired lookahead and that we do not yet have.
    1435              :  *
    1436              :  * @param[in,out] opt our options
    1437              :  * @param[in,out] wake set to true if we should wake the clients
    1438              :  */
    1439              : static void
    1440           39 : create_missing_keys (struct TALER_SECMOD_Options *opt,
    1441              :                      bool *wake)
    1442              : {
    1443              :   struct GNUNET_TIME_Timestamp start;
    1444              :   struct GNUNET_TIME_Timestamp end;
    1445              : 
    1446           39 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1447              :               "Updating denominations ...\n");
    1448           39 :   start = opt->global_now;
    1449           39 :   GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    1450           39 :   end = get_anchor_end ();
    1451           39 :   for (struct Denomination *denom = denom_head;
    1452          179 :        NULL != denom;
    1453          140 :        denom = denom->next)
    1454              :   {
    1455              :     struct GNUNET_TIME_Timestamp anchor_start;
    1456              :     struct GNUNET_TIME_Timestamp anchor_end;
    1457              :     struct GNUNET_TIME_Timestamp next_end;
    1458          140 :     bool finished = false;
    1459              : 
    1460          140 :     remove_expired_denomination_keys (denom);
    1461          140 :     if (NULL != denom->keys_tail)
    1462              :     {
    1463           74 :       anchor_start = denom->keys_tail->anchor_end;
    1464           74 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1465              :                   "Expanding keys of denomination `%s', last key %s valid for another %s\n",
    1466              :                   denom->section,
    1467              :                   GNUNET_h2s (&denom->keys_tail->h_rsa.hash),
    1468              :                   GNUNET_TIME_relative2s (
    1469              :                     GNUNET_TIME_absolute_get_remaining (
    1470              :                       anchor_start.abs_time),
    1471              :                     true));
    1472              :     }
    1473              :     else
    1474              :     {
    1475           66 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1476              :                   "Starting keys of denomination `%s'\n",
    1477              :                   denom->section);
    1478              :       /* Round the very first anchor down to the configured calendar
    1479              :          interval; subsequent anchors inherit the alignment from the
    1480              :          (rounded up) end of the preceding key.  The UTC variants are used
    1481              :          so that the result does not depend on the time zone the secmod
    1482              :          happens to run in. */
    1483           66 :       anchor_start = GNUNET_TIME_absolute_to_timestamp (
    1484              :         GNUNET_TIME_round_down_utc (start.abs_time,
    1485              :                                     denom->anchor_round));
    1486              :     }
    1487          140 :     finished = GNUNET_TIME_timestamp_cmp (anchor_start,
    1488              :                                           >=,
    1489              :                                           end);
    1490          344 :     while (! finished)
    1491              :     {
    1492              :       /* Round the end of the validity period up to the configured calendar
    1493              :          interval.  As #GNUNET_TIME_UNIT_YEARS is 365 days, this is also what
    1494              :          keeps the anchors from drifting off the calendar boundary across leap
    1495              :          years.  Without ANCHOR_ROUND, all of this is a no-op. */
    1496          204 :       anchor_end = GNUNET_TIME_absolute_to_timestamp (
    1497              :         GNUNET_TIME_round_up_utc (
    1498              :           GNUNET_TIME_absolute_add (anchor_start.abs_time,
    1499              :                                     denom->duration_withdraw),
    1500              :           denom->anchor_round));
    1501          204 :       next_end = GNUNET_TIME_absolute_to_timestamp (
    1502              :         GNUNET_TIME_round_up_utc (
    1503              :           GNUNET_TIME_absolute_add (anchor_end.abs_time,
    1504              :                                     denom->duration_withdraw),
    1505              :           denom->anchor_round));
    1506          204 :       if (GNUNET_TIME_timestamp_cmp (next_end,
    1507              :                                      >,
    1508              :                                      end))
    1509              :       {
    1510              :         /* With ANCHOR_ROUND set the calendar interval already provides the
    1511              :            alignment, and stretching the last key would make it cover more
    1512              :            than the one interval it is supposed to cover. */
    1513           68 :         if (GNUNET_TIME_RI_NONE == denom->anchor_round)
    1514           67 :           anchor_end = end; /* extend period to align end periods */
    1515           68 :         finished = true;
    1516              :       }
    1517              :       /* adjust start time down to ensure overlap */
    1518          204 :       anchor_start = GNUNET_TIME_absolute_to_timestamp (
    1519              :         GNUNET_TIME_absolute_subtract (anchor_start.abs_time,
    1520              :                                        overlap_duration));
    1521          204 :       if (! *wake)
    1522              :       {
    1523            2 :         key_gen++;
    1524            2 :         *wake = true;
    1525              :       }
    1526          204 :       if (GNUNET_OK !=
    1527          204 :           create_key (denom,
    1528              :                       anchor_start,
    1529              :                       anchor_end))
    1530              :       {
    1531            0 :         GNUNET_break (0);
    1532            0 :         GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    1533            0 :         globals->global_ret = EXIT_FAILURE;
    1534            0 :         GNUNET_SCHEDULER_shutdown ();
    1535            0 :         return;
    1536              :       }
    1537          204 :       anchor_start = anchor_end;
    1538              :     }
    1539          140 :     remove_expired_denomination_keys (denom);
    1540              :   }
    1541           39 :   GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    1542           39 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1543              :               "Updating denominations finished ...\n");
    1544              : }
    1545              : 
    1546              : 
    1547              : /**
    1548              :  * Task run periodically to expire keys and/or generate fresh ones.
    1549              :  *
    1550              :  * @param cls the `struct TALER_SECMOD_Options *`
    1551              :  */
    1552              : static void
    1553           17 : update_denominations (void *cls)
    1554              : {
    1555           17 :   struct TALER_SECMOD_Options *opt = cls;
    1556              :   struct GNUNET_TIME_Absolute at;
    1557           17 :   bool wake = false;
    1558              : 
    1559              :   (void) cls;
    1560           17 :   keygen_task = NULL;
    1561              :   /* update current time, global override no longer applies */
    1562           17 :   opt->global_now = GNUNET_TIME_timestamp_get ();
    1563           17 :   create_missing_keys (opt,
    1564              :                        &wake);
    1565           17 :   if (wake)
    1566            2 :     TES_wake_clients ();
    1567           17 :   at = action_time ();
    1568           17 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1569              :               "Next key generation due at %s\n",
    1570              :               GNUNET_TIME_absolute2s (at));
    1571           17 :   keygen_task = GNUNET_SCHEDULER_add_at (at,
    1572              :                                          &update_denominations,
    1573              :                                          opt);
    1574           17 : }
    1575              : 
    1576              : 
    1577              : /**
    1578              :  * Parse private key of denomination @a denom in @a buf.
    1579              :  *
    1580              :  * @param[out] denom denomination of the key
    1581              :  * @param filename name of the file we are parsing, for logging
    1582              :  * @param buf key material
    1583              :  * @param buf_size number of bytes in @a buf
    1584              :  */
    1585              : static void
    1586            9 : parse_key (struct Denomination *denom,
    1587              :            const char *filename,
    1588              :            const void *buf,
    1589              :            size_t buf_size)
    1590              : {
    1591              :   struct GNUNET_CRYPTO_RsaPrivateKey *priv;
    1592              :   const char *anchor_s;
    1593              :   char dummy;
    1594              :   unsigned long long anchor_start_ll;
    1595              :   unsigned long long anchor_end_ll;
    1596              :   struct GNUNET_TIME_Timestamp anchor_start;
    1597              :   struct GNUNET_TIME_Timestamp anchor_end;
    1598            9 :   char *nf = NULL;
    1599              : 
    1600            9 :   anchor_s = strrchr (filename,
    1601              :                       '/');
    1602            9 :   if (NULL == anchor_s)
    1603              :   {
    1604              :     /* File in a directory without '/' in the name, this makes no sense. */
    1605            0 :     GNUNET_break (0);
    1606            0 :     return;
    1607              :   }
    1608            9 :   anchor_s++;
    1609            9 :   if (2 != sscanf (anchor_s,
    1610              :                    "%llu-%llu%c",
    1611              :                    &anchor_start_ll,
    1612              :                    &anchor_end_ll,
    1613              :                    &dummy))
    1614              :   {
    1615              :     /* try legacy mode */
    1616            3 :     if (1 != sscanf (anchor_s,
    1617              :                      "%llu%c",
    1618              :                      &anchor_start_ll,
    1619              :                      &dummy))
    1620              :     {
    1621              :       /* Filenames in KEYDIR must ONLY be the anchor time in seconds! */
    1622            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1623              :                   "Filename `%s' invalid for key file, skipping\n",
    1624              :                   anchor_s);
    1625            0 :       return;
    1626              :     }
    1627              :     anchor_start.abs_time.abs_value_us
    1628            3 :       = anchor_start_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
    1629            6 :     if (anchor_start_ll != anchor_start.abs_time.abs_value_us
    1630            3 :         / GNUNET_TIME_UNIT_SECONDS.rel_value_us)
    1631              :     {
    1632              :       /* Integer overflow. Bad, invalid filename. */
    1633            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1634              :                   "Integer overflow. Filename `%s' invalid for key file, skipping\n",
    1635              :                   anchor_s);
    1636            0 :       return;
    1637              :     }
    1638              :     anchor_end
    1639            3 :       = GNUNET_TIME_absolute_to_timestamp (
    1640              :           GNUNET_TIME_absolute_add (anchor_start.abs_time,
    1641              :                                     denom->duration_withdraw));
    1642            3 :     GNUNET_asprintf (
    1643              :       &nf,
    1644              :       "%s/%s/%llu-%llu",
    1645              :       keydir,
    1646              :       denom->section,
    1647              :       anchor_start_ll,
    1648            3 :       (unsigned long long) (anchor_end.abs_time.abs_value_us
    1649            3 :                             / GNUNET_TIME_UNIT_SECONDS.rel_value_us));
    1650              :     /* Try to fix the legacy filename */
    1651            3 :     if (0 !=
    1652            3 :         rename (filename,
    1653              :                 nf))
    1654              :     {
    1655            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    1656              :                                 "rename",
    1657              :                                 filename);
    1658            0 :       GNUNET_free (nf);
    1659              :     }
    1660              :   }
    1661              :   else
    1662              :   {
    1663              :     anchor_start.abs_time.abs_value_us
    1664            6 :       = anchor_start_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
    1665              :     anchor_end.abs_time.abs_value_us
    1666            6 :       = anchor_end_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
    1667           12 :     if ( (anchor_start_ll != anchor_start.abs_time.abs_value_us
    1668            6 :           / GNUNET_TIME_UNIT_SECONDS.rel_value_us) ||
    1669           12 :          (anchor_end_ll != anchor_end.abs_time.abs_value_us
    1670            6 :           / GNUNET_TIME_UNIT_SECONDS.rel_value_us) )
    1671              :     {
    1672              :       /* Integer overflow. Bad, invalid filename. */
    1673            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1674              :                   "Integer overflow. Filename `%s' invalid for key file, skipping\n",
    1675              :                   anchor_s);
    1676            0 :       return;
    1677              :     }
    1678              :   }
    1679            9 :   priv = GNUNET_CRYPTO_rsa_private_key_decode (buf,
    1680              :                                                buf_size);
    1681            9 :   if (NULL == priv)
    1682              :   {
    1683              :     /* Parser failure. */
    1684            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1685              :                 "File `%s' is malformed, skipping\n",
    1686              :                 (NULL == nf) ? filename : nf);
    1687            0 :     GNUNET_free (nf);
    1688            0 :     return;
    1689              :   }
    1690              : 
    1691              :   {
    1692              :     struct GNUNET_CRYPTO_RsaPublicKey *pub;
    1693              :     struct DenominationKey *dk;
    1694              :     struct DenominationKey *before;
    1695              : 
    1696            9 :     pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv);
    1697            9 :     if (NULL == pub)
    1698              :     {
    1699            0 :       GNUNET_break (0);
    1700            0 :       GNUNET_CRYPTO_rsa_private_key_free (priv);
    1701            0 :       GNUNET_free (nf);
    1702            0 :       return;
    1703              :     }
    1704            9 :     dk = GNUNET_new (struct DenominationKey);
    1705            9 :     dk->denom_priv = priv;
    1706            9 :     dk->denom = denom;
    1707            9 :     dk->anchor_start = anchor_start;
    1708            9 :     dk->anchor_end = anchor_end;
    1709            9 :     dk->filename = (NULL == nf) ? GNUNET_strdup (filename) : nf;
    1710            9 :     GNUNET_CRYPTO_rsa_public_key_hash (pub,
    1711              :                                        &dk->h_rsa.hash);
    1712            9 :     dk->denom_pub = pub;
    1713            9 :     generate_response (dk);
    1714            9 :     if (GNUNET_OK !=
    1715            9 :         GNUNET_CONTAINER_multihashmap_put (
    1716              :           keys,
    1717            9 :           &dk->h_rsa.hash,
    1718              :           dk,
    1719              :           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
    1720              :     {
    1721            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1722              :                   "Duplicate private key %s detected in file `%s'. Skipping.\n",
    1723              :                   GNUNET_h2s (&dk->h_rsa.hash),
    1724              :                   filename);
    1725            0 :       GNUNET_CRYPTO_rsa_private_key_free (priv);
    1726            0 :       GNUNET_CRYPTO_rsa_public_key_free (pub);
    1727            0 :       GNUNET_free (dk->an);
    1728            0 :       GNUNET_free (dk);
    1729            0 :       return;
    1730              :     }
    1731            9 :     before = NULL;
    1732            9 :     for (struct DenominationKey *pos = denom->keys_head;
    1733           10 :          NULL != pos;
    1734            1 :          pos = pos->next)
    1735              :     {
    1736            6 :       if (GNUNET_TIME_timestamp_cmp (pos->anchor_start,
    1737              :                                      >,
    1738              :                                      anchor_start))
    1739            5 :         break;
    1740            1 :       before = pos;
    1741              :     }
    1742            9 :     GNUNET_CONTAINER_DLL_insert_after (denom->keys_head,
    1743              :                                        denom->keys_tail,
    1744              :                                        before,
    1745              :                                        dk);
    1746            9 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1747              :                 "Imported key %s from `%s'\n",
    1748              :                 GNUNET_h2s (&dk->h_rsa.hash),
    1749              :                 filename);
    1750              :   }
    1751              : }
    1752              : 
    1753              : 
    1754              : /**
    1755              :  * Import a private key from @a filename for the denomination
    1756              :  * given in @a cls.
    1757              :  *
    1758              :  * @param[in,out] cls a `struct Denomiantion`
    1759              :  * @param filename name of a file in the directory
    1760              :  * @return #GNUNET_OK (always, continue to iterate)
    1761              :  */
    1762              : static enum GNUNET_GenericReturnValue
    1763            9 : import_key (void *cls,
    1764              :             const char *filename)
    1765              : {
    1766            9 :   struct Denomination *denom = cls;
    1767              :   struct GNUNET_DISK_FileHandle *fh;
    1768              :   struct GNUNET_DISK_MapHandle *map;
    1769              :   void *ptr;
    1770              :   int fd;
    1771              :   struct stat sbuf;
    1772              : 
    1773              :   {
    1774              :     struct stat lsbuf;
    1775              : 
    1776            9 :     if (0 != lstat (filename,
    1777              :                     &lsbuf))
    1778              :     {
    1779            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    1780              :                                 "lstat",
    1781              :                                 filename);
    1782            0 :       return GNUNET_OK;
    1783              :     }
    1784            9 :     if (! S_ISREG (lsbuf.st_mode))
    1785              :     {
    1786            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1787              :                   "File `%s' is not a regular file, which is not allowed for private keys!\n",
    1788              :                   filename);
    1789            0 :       return GNUNET_OK;
    1790              :     }
    1791              :   }
    1792              : 
    1793            9 :   fd = open (filename,
    1794              :              O_RDONLY | O_CLOEXEC);
    1795            9 :   if (-1 == fd)
    1796              :   {
    1797            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    1798              :                               "open",
    1799              :                               filename);
    1800            0 :     return GNUNET_OK;
    1801              :   }
    1802            9 :   if (0 != fstat (fd,
    1803              :                   &sbuf))
    1804              :   {
    1805            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    1806              :                               "stat",
    1807              :                               filename);
    1808            0 :     GNUNET_break (0 == close (fd));
    1809            0 :     return GNUNET_OK;
    1810              :   }
    1811            9 :   if (! S_ISREG (sbuf.st_mode))
    1812              :   {
    1813            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1814              :                 "File `%s' is not a regular file, which is not allowed for private keys!\n",
    1815              :                 filename);
    1816            0 :     GNUNET_break (0 == close (fd));
    1817            0 :     return GNUNET_OK;
    1818              :   }
    1819            9 :   if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO)))
    1820              :   {
    1821              :     /* permission are NOT tight, try to patch them up! */
    1822            0 :     if (0 !=
    1823            0 :         fchmod (fd,
    1824              :                 S_IRUSR))
    1825              :     {
    1826            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    1827              :                                 "fchmod",
    1828              :                                 filename);
    1829              :       /* refuse to use key if file has wrong permissions */
    1830            0 :       GNUNET_break (0 == close (fd));
    1831            0 :       return GNUNET_OK;
    1832              :     }
    1833              :   }
    1834            9 :   fh = GNUNET_DISK_get_handle_from_int_fd (fd);
    1835            9 :   if (NULL == fh)
    1836              :   {
    1837            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    1838              :                               "open",
    1839              :                               filename);
    1840            0 :     GNUNET_break (0 == close (fd));
    1841            0 :     return GNUNET_OK;
    1842              :   }
    1843            9 :   if (sbuf.st_size > 16 * 1024)
    1844              :   {
    1845            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1846              :                 "File `%s' too big to be a private key\n",
    1847              :                 filename);
    1848            0 :     GNUNET_DISK_file_close (fh);
    1849            0 :     return GNUNET_OK;
    1850              :   }
    1851            9 :   ptr = GNUNET_DISK_file_map (fh,
    1852              :                               &map,
    1853              :                               GNUNET_DISK_MAP_TYPE_READ,
    1854            9 :                               (size_t) sbuf.st_size);
    1855            9 :   if (NULL == ptr)
    1856              :   {
    1857            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
    1858              :                               "mmap",
    1859              :                               filename);
    1860            0 :     GNUNET_DISK_file_close (fh);
    1861            0 :     return GNUNET_OK;
    1862              :   }
    1863            9 :   parse_key (denom,
    1864              :              filename,
    1865              :              ptr,
    1866            9 :              (size_t) sbuf.st_size);
    1867            9 :   GNUNET_DISK_file_unmap (map);
    1868            9 :   GNUNET_DISK_file_close (fh);
    1869            9 :   return GNUNET_OK;
    1870              : }
    1871              : 
    1872              : 
    1873              : /**
    1874              :  * Parse configuration for denomination type parameters.  Also determines
    1875              :  * our anchor by looking at the existing denominations of the same type.
    1876              :  *
    1877              :  * @param cfg configuration to use
    1878              :  * @param ct section in the configuration file giving the denomination type parameters
    1879              :  * @param[out] denom set to the denomination parameters from the configuration
    1880              :  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the configuration is invalid
    1881              :  */
    1882              : static enum GNUNET_GenericReturnValue
    1883           69 : parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
    1884              :                         const char *ct,
    1885              :                         struct Denomination *denom)
    1886              : {
    1887              :   unsigned long long rsa_keysize;
    1888              :   char *secname;
    1889              : 
    1890           69 :   GNUNET_asprintf (&secname,
    1891              :                    "%s-secmod-rsa",
    1892           69 :                    globals->section);
    1893           69 :   if (GNUNET_OK !=
    1894           69 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    1895              :                                            ct,
    1896              :                                            "DURATION_WITHDRAW",
    1897              :                                            &denom->duration_withdraw))
    1898              :   {
    1899            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1900              :                                ct,
    1901              :                                "DURATION_WITHDRAW");
    1902            0 :     GNUNET_free (secname);
    1903            0 :     return GNUNET_SYSERR;
    1904              :   }
    1905           69 :   if (GNUNET_TIME_relative_cmp (denom->duration_withdraw,
    1906              :                                 <,
    1907              :                                 GNUNET_TIME_UNIT_SECONDS))
    1908              :   {
    1909            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    1910              :                                ct,
    1911              :                                "DURATION_WITHDRAW",
    1912              :                                "less than one second is not supported");
    1913            0 :     GNUNET_free (secname);
    1914            0 :     return GNUNET_SYSERR;
    1915              :   }
    1916           69 :   if (GNUNET_TIME_relative_cmp (overlap_duration,
    1917              :                                 >=,
    1918              :                                 denom->duration_withdraw))
    1919              :   {
    1920            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    1921              :                                secname,
    1922              :                                "OVERLAP_DURATION",
    1923              :                                "Value given must be smaller than value for DURATION_WITHDRAW!");
    1924            0 :     GNUNET_free (secname);
    1925            0 :     return GNUNET_SYSERR;
    1926              :   }
    1927              :   {
    1928              :     struct GNUNET_TIME_Relative ar;
    1929              : 
    1930              :     /* The denomination section takes precedence, the secmod section
    1931              :        provides the default for all denominations. */
    1932           69 :     if ( (GNUNET_OK !=
    1933           69 :           GNUNET_CONFIGURATION_get_value_time (cfg,
    1934              :                                                ct,
    1935              :                                                "ANCHOR_ROUND",
    1936           65 :                                                &ar)) &&
    1937              :          (GNUNET_OK !=
    1938           65 :           GNUNET_CONFIGURATION_get_value_time (cfg,
    1939              :                                                secname,
    1940              :                                                "ANCHOR_ROUND",
    1941              :                                                &ar)) )
    1942            0 :       ar = GNUNET_TIME_UNIT_ZERO; /* not configured: do not round */
    1943              :     denom->anchor_round
    1944           69 :       = GNUNET_TIME_relative_to_round_interval (ar);
    1945           69 :     if ( (GNUNET_TIME_RI_NONE == denom->anchor_round) &&
    1946           65 :          (! GNUNET_TIME_relative_is_zero (ar)) )
    1947              :     {
    1948            0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    1949              :                                  ct,
    1950              :                                  "ANCHOR_ROUND",
    1951              :                                  "Value given must be zero or exactly one second, minute, hour, day, week, month, quarter or year");
    1952            0 :       GNUNET_free (secname);
    1953            0 :       return GNUNET_SYSERR;
    1954              :     }
    1955              :   }
    1956           69 :   if (GNUNET_OK !=
    1957           69 :       GNUNET_CONFIGURATION_get_value_number (cfg,
    1958              :                                              ct,
    1959              :                                              "RSA_KEYSIZE",
    1960              :                                              &rsa_keysize))
    1961              :   {
    1962            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1963              :                                ct,
    1964              :                                "RSA_KEYSIZE");
    1965            0 :     GNUNET_free (secname);
    1966            0 :     return GNUNET_SYSERR;
    1967              :   }
    1968           69 :   if ( (rsa_keysize > 4 * 2048) ||
    1969           69 :        (rsa_keysize < 1024) )
    1970              :   {
    1971            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    1972              :                                ct,
    1973              :                                "RSA_KEYSIZE",
    1974              :                                "Given RSA keysize outside of permitted range [1024,8192]\n");
    1975            0 :     GNUNET_free (secname);
    1976            0 :     return GNUNET_SYSERR;
    1977              :   }
    1978           69 :   GNUNET_free (secname);
    1979           69 :   denom->rsa_keysize = (unsigned int) rsa_keysize;
    1980           69 :   denom->section = TES_normalize_section (ct);
    1981           69 :   return GNUNET_OK;
    1982              : }
    1983              : 
    1984              : 
    1985              : /**
    1986              :  * Closure for #load_denominations.
    1987              :  */
    1988              : struct LoadContext
    1989              : {
    1990              : 
    1991              :   /**
    1992              :    * Configuration to use.
    1993              :    */
    1994              :   const struct GNUNET_CONFIGURATION_Handle *cfg;
    1995              : 
    1996              :   /**
    1997              :    * Configuration section prefix to use for denomination settings.
    1998              :    * "coin_" for the exchange, "doco_" for Donau.
    1999              :    */
    2000              :   const char *cprefix;
    2001              : 
    2002              :   /**
    2003              :    * Status, to be set to #GNUNET_SYSERR on failure
    2004              :    */
    2005              :   enum GNUNET_GenericReturnValue ret;
    2006              : };
    2007              : 
    2008              : 
    2009              : /**
    2010              :  * Generate new denomination signing keys for the denomination type of the given @a
    2011              :  * denomination_alias.
    2012              :  *
    2013              :  * @param cls a `struct LoadContext`, with 'ret' to be set to #GNUNET_SYSERR on failure
    2014              :  * @param denomination_alias name of the denomination's section in the configuration
    2015              :  */
    2016              : static void
    2017          903 : load_denominations (void *cls,
    2018              :                     const char *denomination_alias)
    2019              : {
    2020          903 :   struct LoadContext *ctx = cls;
    2021              :   struct Denomination *denom;
    2022              :   char *cipher;
    2023              : 
    2024          903 :   if (0 != strncasecmp (denomination_alias,
    2025              :                         ctx->cprefix,
    2026              :                         strlen (ctx->cprefix)))
    2027          834 :     return; /* not a denomination type definition */
    2028          121 :   if (GNUNET_OK !=
    2029          121 :       GNUNET_CONFIGURATION_get_value_string (ctx->cfg,
    2030              :                                              denomination_alias,
    2031              :                                              "CIPHER",
    2032              :                                              &cipher))
    2033              :   {
    2034            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    2035              :                                denomination_alias,
    2036              :                                "CIPHER");
    2037            0 :     return;
    2038              :   }
    2039          121 :   if (0 != strcmp (cipher,
    2040              :                    "RSA"))
    2041              :   {
    2042           52 :     GNUNET_free (cipher);
    2043           52 :     return; /* Ignore denominations of other types than CS */
    2044              :   }
    2045           69 :   GNUNET_free (cipher);
    2046           69 :   denom = GNUNET_new (struct Denomination);
    2047           69 :   if (GNUNET_OK !=
    2048           69 :       parse_denomination_cfg (ctx->cfg,
    2049              :                               denomination_alias,
    2050              :                               denom))
    2051              :   {
    2052            0 :     ctx->ret = GNUNET_SYSERR;
    2053            0 :     GNUNET_free (denom);
    2054            0 :     return;
    2055              :   }
    2056           69 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2057              :               "Loading keys for denomination %s\n",
    2058              :               denom->section);
    2059              :   {
    2060              :     char *dname;
    2061              : 
    2062           69 :     GNUNET_asprintf (&dname,
    2063              :                      "%s/%s",
    2064              :                      keydir,
    2065              :                      denom->section);
    2066           69 :     GNUNET_break (GNUNET_OK ==
    2067              :                   GNUNET_DISK_directory_create (dname));
    2068           69 :     GNUNET_DISK_directory_scan (dname,
    2069              :                                 &import_key,
    2070              :                                 denom);
    2071           69 :     GNUNET_free (dname);
    2072              :   }
    2073           69 :   GNUNET_CONTAINER_DLL_insert (denom_head,
    2074              :                                denom_tail,
    2075              :                                denom);
    2076              : }
    2077              : 
    2078              : 
    2079              : /**
    2080              :  * Load the various duration values from @a cfg
    2081              :  *
    2082              :  * @param cfg configuration to use
    2083              :  * @return #GNUNET_OK on success
    2084              :  */
    2085              : static enum GNUNET_GenericReturnValue
    2086           35 : load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg)
    2087              : {
    2088              :   char *secname;
    2089              : 
    2090           35 :   GNUNET_asprintf (&secname,
    2091              :                    "%s-secmod-rsa",
    2092           35 :                    globals->section);
    2093           35 :   if (GNUNET_OK !=
    2094           35 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    2095              :                                            secname,
    2096              :                                            "OVERLAP_DURATION",
    2097              :                                            &overlap_duration))
    2098              :   {
    2099            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    2100              :                                secname,
    2101              :                                "OVERLAP_DURATION");
    2102            0 :     GNUNET_free (secname);
    2103            0 :     return GNUNET_SYSERR;
    2104              :   }
    2105           35 :   if (GNUNET_OK !=
    2106           35 :       GNUNET_CONFIGURATION_get_value_time (cfg,
    2107              :                                            secname,
    2108              :                                            "LOOKAHEAD_SIGN",
    2109              :                                            &lookahead_sign))
    2110              :   {
    2111            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    2112              :                                secname,
    2113              :                                "LOOKAHEAD_SIGN");
    2114            0 :     GNUNET_free (secname);
    2115            0 :     return GNUNET_SYSERR;
    2116              :   }
    2117           35 :   GNUNET_free (secname);
    2118           35 :   return GNUNET_OK;
    2119              : }
    2120              : 
    2121              : 
    2122              : /**
    2123              :  * Function run on shutdown. Stops the various jobs (nicely).
    2124              :  *
    2125              :  * @param cls NULL
    2126              :  */
    2127              : static void
    2128           22 : do_shutdown (void *cls)
    2129              : {
    2130              :   (void) cls;
    2131           22 :   TES_listen_stop ();
    2132           22 :   if (NULL != keygen_task)
    2133              :   {
    2134           15 :     GNUNET_SCHEDULER_cancel (keygen_task);
    2135           15 :     keygen_task = NULL;
    2136              :   }
    2137           22 :   stop_workers ();
    2138           22 :   sem_done (&worker_sem);
    2139           22 : }
    2140              : 
    2141              : 
    2142              : void
    2143           35 : TALER_SECMOD_rsa_run (void *cls,
    2144              :                       char *const *args,
    2145              :                       const char *cfgfile,
    2146              :                       const struct GNUNET_CONFIGURATION_Handle *cfg)
    2147              : {
    2148              :   static struct TES_Callbacks cb = {
    2149              :     .dispatch = rsa_work_dispatch,
    2150              :     .updater = rsa_update_client_keys,
    2151              :     .init = rsa_client_init
    2152              :   };
    2153           35 :   struct TALER_SECMOD_Options *opt = cls;
    2154              :   char *secname;
    2155              : 
    2156              :   (void) args;
    2157              :   (void) cfgfile;
    2158           35 :   globals = opt;
    2159           35 :   if (GNUNET_TIME_timestamp_cmp (opt->global_now,
    2160              :                                  !=,
    2161              :                                  opt->global_now_tmp))
    2162              :   {
    2163              :     /* The user gave "--now", use it! */
    2164            0 :     opt->global_now = opt->global_now_tmp;
    2165              :   }
    2166              :   else
    2167              :   {
    2168              :     /* get current time again, we may be timetraveling! */
    2169           35 :     opt->global_now = GNUNET_TIME_timestamp_get ();
    2170              :   }
    2171           35 :   GNUNET_asprintf (&secname,
    2172              :                    "%s-secmod-rsa",
    2173              :                    opt->section);
    2174           35 :   if (GNUNET_OK !=
    2175           35 :       GNUNET_CONFIGURATION_get_value_filename (cfg,
    2176              :                                                secname,
    2177              :                                                "KEY_DIR",
    2178              :                                                &keydir))
    2179              :   {
    2180            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    2181              :                                secname,
    2182              :                                "KEY_DIR");
    2183            0 :     GNUNET_free (secname);
    2184            0 :     opt->global_ret = EXIT_NOTCONFIGURED;
    2185           20 :     return;
    2186              :   }
    2187           35 :   if (GNUNET_OK !=
    2188           35 :       load_durations (cfg))
    2189              :   {
    2190            0 :     opt->global_ret = EXIT_NOTCONFIGURED;
    2191            0 :     GNUNET_free (secname);
    2192            0 :     return;
    2193              :   }
    2194           35 :   if (GNUNET_OK !=
    2195           35 :       TES_normalize_key_directory (cfg,
    2196              :                                    keydir,
    2197              :                                    opt->cprefix))
    2198              :   {
    2199           13 :     opt->global_ret = EXIT_FAILURE;
    2200           13 :     GNUNET_free (secname);
    2201           13 :     return;
    2202              :   }
    2203           22 :   opt->global_ret = TES_listen_start (cfg,
    2204              :                                       secname,
    2205              :                                       &cb);
    2206           22 :   GNUNET_free (secname);
    2207           22 :   if (0 != opt->global_ret)
    2208            0 :     return;
    2209           22 :   sem_init (&worker_sem,
    2210              :             0);
    2211           22 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    2212              :                                  NULL);
    2213           22 :   if (0 == opt->max_workers)
    2214              :   {
    2215              :     long lret;
    2216              : 
    2217            0 :     lret = sysconf (_SC_NPROCESSORS_CONF);
    2218            0 :     if (lret <= 0)
    2219            0 :       lret = 1;
    2220            0 :     opt->max_workers = (unsigned int) lret;
    2221              :   }
    2222              : 
    2223          314 :   for (unsigned int i = 0; i<opt->max_workers; i++)
    2224          292 :     if (GNUNET_OK !=
    2225          292 :         start_worker ())
    2226              :     {
    2227            0 :       GNUNET_SCHEDULER_shutdown ();
    2228            0 :       return;
    2229              :     }
    2230              :   /* Load denominations */
    2231           22 :   keys = GNUNET_CONTAINER_multihashmap_create (65536,
    2232              :                                                true);
    2233              :   {
    2234           22 :     struct LoadContext lc = {
    2235              :       .cfg = cfg,
    2236              :       .ret = GNUNET_OK,
    2237           22 :       .cprefix = opt->cprefix
    2238              :     };
    2239           22 :     bool wake = true;
    2240              : 
    2241           22 :     GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
    2242           22 :     GNUNET_CONFIGURATION_iterate_sections (cfg,
    2243              :                                            &load_denominations,
    2244              :                                            &lc);
    2245           22 :     GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
    2246           22 :     if (GNUNET_OK != lc.ret)
    2247              :     {
    2248            0 :       opt->global_ret = EXIT_FAILURE;
    2249            0 :       GNUNET_SCHEDULER_shutdown ();
    2250            0 :       return;
    2251              :     }
    2252           22 :     create_missing_keys (opt,
    2253              :                          &wake);
    2254              :   }
    2255           22 :   if (NULL == denom_head)
    2256              :   {
    2257            7 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2258              :                 "No RSA denominations configured. Make sure section names start with `%s' if you are using RSA!\n",
    2259              :                 opt->cprefix);
    2260            7 :     TES_wake_clients ();
    2261            7 :     return;
    2262              :   }
    2263              :   /* start job to keep keys up-to-date; MUST be run before the #listen_task,
    2264              :      hence with priority. */
    2265           15 :   keygen_task = GNUNET_SCHEDULER_add_with_priority (
    2266              :     GNUNET_SCHEDULER_PRIORITY_URGENT,
    2267              :     &update_denominations,
    2268              :     opt);
    2269              : }
        

Generated by: LCOV version 2.0-1