exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

test_helper_cs.c (36611B)


      1 /*
      2   This file is part of TALER
      3   (C) 2020, 2021, 2023 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/test_helper_cs.c
     18  * @brief Tests for CS crypto helper
     19  * @author Christian Grothoff
     20  */
     21 #include "taler/platform.h"
     22 #include "taler/taler_util.h"
     23 
     24 /**
     25  * Configuration has 1 minute duration and 5 minutes lookahead, but
     26  * we do not get 'revocations' for expired keys. So this must be
     27  * large enough to deal with key rotation during the runtime of
     28  * the benchmark.
     29  */
     30 #define MAX_KEYS 1024
     31 
     32 /**
     33  * How many random key revocations should we test?
     34  */
     35 #define NUM_REVOKES 3
     36 
     37 /**
     38  * How many iterations of the successful signing test should we run?
     39  */
     40 #define NUM_SIGN_TESTS 5
     41 
     42 /**
     43  * How many iterations of the successful signing test should we run
     44  * during the benchmark phase?
     45  */
     46 #define NUM_SIGN_PERFS 100
     47 
     48 /**
     49  * How many parallel clients should we use for the parallel
     50  * benchmark? (> 500 may cause problems with the max open FD number limit).
     51  */
     52 #define NUM_CORES 8
     53 
     54 /**
     55  * Number of keys currently in #keys.
     56  */
     57 static unsigned int num_keys;
     58 
     59 /**
     60  * Keys currently managed by the helper.
     61  */
     62 struct KeyData
     63 {
     64   /**
     65    * Validity start point.
     66    */
     67   struct GNUNET_TIME_Timestamp start_time;
     68 
     69   /**
     70    * Key expires for signing at @e start_time plus this value.
     71    */
     72   struct GNUNET_TIME_Relative validity_duration;
     73 
     74   /**
     75    * Hash of the public key.
     76    */
     77   struct TALER_CsPubHashP h_cs;
     78 
     79   /**
     80    * Full public key.
     81    */
     82   struct TALER_DenominationPublicKey denom_pub;
     83 
     84   /**
     85    * Is this key currently valid?
     86    */
     87   bool valid;
     88 
     89   /**
     90    * Did the test driver revoke this key?
     91    */
     92   bool revoked;
     93 };
     94 
     95 /**
     96  * Array of all the keys we got from the helper.
     97  */
     98 static struct KeyData keys[MAX_KEYS];
     99 
    100 
    101 /**
    102  * Release memory occupied by #keys.
    103  */
    104 static void
    105 free_keys (void)
    106 {
    107   for (unsigned int i = 0; i<MAX_KEYS; i++)
    108     if (keys[i].valid)
    109     {
    110       TALER_denom_pub_free (&keys[i].denom_pub);
    111       keys[i].valid = false;
    112       GNUNET_assert (num_keys > 0);
    113       num_keys--;
    114     }
    115 }
    116 
    117 
    118 /**
    119  * Function called with information about available keys for signing.  Usually
    120  * only called once per key upon connect. Also called again in case a key is
    121  * being revoked, in that case with an @a end_time of zero.  Stores the keys
    122  * status in #keys.
    123  *
    124  * @param cls closure, NULL
    125  * @param section_name name of the denomination type in the configuration;
    126  *                 NULL if the key has been revoked or purged
    127  * @param start_time when does the key become available for signing;
    128  *                 zero if the key has been revoked or purged
    129  * @param validity_duration how long does the key remain available for signing;
    130  *                 zero if the key has been revoked or purged
    131  * @param h_cs hash of the @a denom_pub that is available (or was purged)
    132  * @param bs_pub the public key itself, NULL if the key was revoked or purged
    133  * @param sm_pub public key of the security module, NULL if the key was revoked or purged
    134  * @param sm_sig signature from the security module, NULL if the key was revoked or purged
    135  *               The signature was already verified against @a sm_pub.
    136  */
    137 static void
    138 key_cb (void *cls,
    139         const char *section_name,
    140         struct GNUNET_TIME_Timestamp start_time,
    141         struct GNUNET_TIME_Relative validity_duration,
    142         const struct TALER_CsPubHashP *h_cs,
    143         struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub,
    144         const struct TALER_SecurityModulePublicKeyP *sm_pub,
    145         const struct TALER_SecurityModuleSignatureP *sm_sig)
    146 {
    147   (void) cls;
    148   (void) sm_pub;
    149   (void) sm_sig;
    150   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    151               "Key notification about key %s in `%s'\n",
    152               GNUNET_h2s (&h_cs->hash),
    153               section_name);
    154   if (0 == validity_duration.rel_value_us)
    155   {
    156     bool found = false;
    157 
    158     GNUNET_break (NULL == bs_pub);
    159     GNUNET_break (NULL == section_name);
    160     for (unsigned int i = 0; i<MAX_KEYS; i++)
    161       if (0 == GNUNET_memcmp (h_cs,
    162                               &keys[i].h_cs))
    163       {
    164         keys[i].valid = false;
    165         keys[i].revoked = false;
    166         TALER_denom_pub_free (&keys[i].denom_pub);
    167         GNUNET_assert (num_keys > 0);
    168         num_keys--;
    169         found = true;
    170         break;
    171       }
    172     if (! found)
    173       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    174                   "Error: helper announced expiration of unknown key!\n");
    175 
    176     return;
    177   }
    178 
    179   GNUNET_break (NULL != bs_pub);
    180   for (unsigned int i = 0; i<MAX_KEYS; i++)
    181     if (! keys[i].valid)
    182     {
    183       keys[i].valid = true;
    184       keys[i].h_cs = *h_cs;
    185       keys[i].start_time = start_time;
    186       keys[i].validity_duration = validity_duration;
    187       keys[i].denom_pub.bsign_pub_key
    188         = GNUNET_CRYPTO_bsign_pub_incref (bs_pub);
    189       num_keys++;
    190       return;
    191     }
    192   /* too many keys! */
    193   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    194               "Error: received %d live keys from the service!\n",
    195               MAX_KEYS + 1);
    196 }
    197 
    198 
    199 /**
    200  * Test key revocation logic.
    201  *
    202  * @param dh handle to the helper
    203  * @return 0 on success
    204  */
    205 static int
    206 test_revocation (struct TALER_CRYPTO_CsDenominationHelper *dh)
    207 {
    208   struct timespec req = {
    209     .tv_nsec = 250000000
    210   };
    211 
    212   for (unsigned int i = 0; i<NUM_REVOKES; i++)
    213   {
    214     uint32_t off;
    215 
    216     off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
    217                                     num_keys);
    218     /* find index of key to revoke */
    219     for (unsigned int j = 0; j < MAX_KEYS; j++)
    220     {
    221       if (! keys[j].valid)
    222         continue;
    223       if (0 != off)
    224       {
    225         off--;
    226         continue;
    227       }
    228       keys[j].revoked = true;
    229       fprintf (stderr,
    230                "Revoking key %s ...",
    231                GNUNET_h2s (&keys[j].h_cs.hash));
    232       TALER_CRYPTO_helper_cs_revoke (dh,
    233                                      &keys[j].h_cs);
    234       for (unsigned int k = 0; k<1000; k++)
    235       {
    236         TALER_CRYPTO_helper_cs_poll (dh);
    237         if (! keys[j].revoked)
    238           break;
    239         nanosleep (&req, NULL);
    240         fprintf (stderr, ".");
    241       }
    242       if (keys[j].revoked)
    243       {
    244         fprintf (stderr,
    245                  "\nFAILED: timeout trying to revoke key %u\n",
    246                  j);
    247         TALER_CRYPTO_helper_cs_disconnect (dh);
    248         return 2;
    249       }
    250       fprintf (stderr, "\n");
    251       break;
    252     }
    253   }
    254   return 0;
    255 }
    256 
    257 
    258 /**
    259  * Test R derivation logic.
    260  *
    261  * @param dh handle to the helper
    262  * @return 0 on success
    263  */
    264 static int
    265 test_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh)
    266 {
    267   enum TALER_ErrorCode ec;
    268   bool success = false;
    269   struct TALER_PlanchetMasterSecretP ps;
    270   struct TALER_CoinSpendPrivateKeyP coin_priv;
    271   union GNUNET_CRYPTO_BlindingSecretP bks;
    272   struct TALER_CoinPubHashP c_hash;
    273   struct GNUNET_CRYPTO_BlindingInputValues bi = {
    274     .cipher = GNUNET_CRYPTO_BSA_CS
    275   };
    276   struct TALER_ExchangeBlindingValues alg_values = {
    277     .blinding_inputs = &bi
    278   };
    279   union GNUNET_CRYPTO_BlindSessionNonce nonce;
    280 
    281   TALER_planchet_master_setup_random (&ps);
    282   for (unsigned int i = 0; i<MAX_KEYS; i++)
    283   {
    284     struct TALER_PlanchetDetail pd;
    285 
    286     if (! keys[i].valid)
    287       continue;
    288     GNUNET_assert (GNUNET_CRYPTO_BSA_CS ==
    289                    keys[i].denom_pub.bsign_pub_key->cipher);
    290 #pragma message "phase out TALER_cs_withdraw_nonce_derive"
    291     TALER_cs_withdraw_nonce_derive (
    292       &ps,
    293       &nonce.cs_nonce);
    294     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    295                 "Requesting R derivation with key %s\n",
    296                 GNUNET_h2s (&keys[i].h_cs.hash));
    297     {
    298       struct TALER_CRYPTO_CsDeriveRequest cdr = {
    299         .h_cs = &keys[i].h_cs,
    300         .nonce = &nonce.cs_nonce
    301       };
    302 
    303       ec = TALER_CRYPTO_helper_cs_r_derive (
    304         dh,
    305         &cdr,
    306         false,
    307         &bi.details.cs_values);
    308     }
    309     switch (ec)
    310     {
    311     case TALER_EC_NONE:
    312       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
    313                                       keys[i].start_time.abs_time),
    314                                     >,
    315                                     GNUNET_TIME_UNIT_SECONDS))
    316       {
    317         /* key worked too early */
    318         GNUNET_break (0);
    319         return 4;
    320       }
    321       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
    322                                       keys[i].start_time.abs_time),
    323                                     >,
    324                                     keys[i].validity_duration))
    325       {
    326         /* key worked too later */
    327         GNUNET_break (0);
    328         return 5;
    329       }
    330 
    331       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    332                   "Received valid R for key %s\n",
    333                   GNUNET_h2s (&keys[i].h_cs.hash));
    334       TALER_planchet_setup_coin_priv (&ps,
    335                                       &alg_values,
    336                                       &coin_priv);
    337       TALER_planchet_blinding_secret_create (&ps,
    338                                              &alg_values,
    339                                              &bks);
    340       GNUNET_assert (GNUNET_OK ==
    341                      TALER_planchet_prepare (&keys[i].denom_pub,
    342                                              &alg_values,
    343                                              &bks,
    344                                              &nonce,
    345                                              &coin_priv,
    346                                              NULL, /* no age commitment */
    347                                              &c_hash,
    348                                              &pd));
    349       TALER_blinded_planchet_free (&pd.blinded_planchet);
    350       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    351                   "Successfully prepared planchet");
    352       success = true;
    353       break;
    354     case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
    355       /* This 'failure' is expected, we're testing also for the
    356          error handling! */
    357       if ( (GNUNET_TIME_relative_is_zero (
    358               GNUNET_TIME_absolute_get_remaining (
    359                 keys[i].start_time.abs_time))) &&
    360            (GNUNET_TIME_relative_cmp (
    361               GNUNET_TIME_absolute_get_duration (
    362                 keys[i].start_time.abs_time),
    363               <,
    364               keys[i].validity_duration)) )
    365       {
    366         /* key should have worked! */
    367         GNUNET_break (0);
    368         return 6;
    369       }
    370       break;
    371     default:
    372       /* unexpected error */
    373       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    374                   "Unexpected error %d\n",
    375                   ec);
    376       return 7;
    377     }
    378   }
    379   if (! success)
    380   {
    381     /* no valid key for signing found, also bad */
    382     GNUNET_break (0);
    383     return 16;
    384   }
    385 
    386   /* check R derivation does not work if the key is unknown */
    387   {
    388     struct TALER_CsPubHashP rnd;
    389     struct GNUNET_CRYPTO_CSPublicRPairP crp;
    390     struct TALER_CRYPTO_CsDeriveRequest cdr = {
    391       .h_cs = &rnd,
    392       .nonce = &nonce.cs_nonce,
    393     };
    394 
    395     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
    396                                 &rnd,
    397                                 sizeof (rnd));
    398     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
    399                                 &nonce,
    400                                 sizeof (nonce));
    401     ec = TALER_CRYPTO_helper_cs_r_derive (dh,
    402                                           &cdr,
    403                                           false,
    404                                           &crp);
    405     if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
    406     {
    407       GNUNET_break (0);
    408       return 17;
    409     }
    410     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    411                 "R derivation with invalid key %s failed as desired\n",
    412                 GNUNET_h2s (&rnd.hash));
    413   }
    414   return 0;
    415 }
    416 
    417 
    418 /**
    419  * Test signing logic.
    420  *
    421  * @param dh handle to the helper
    422  * @return 0 on success
    423  */
    424 static int
    425 test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh)
    426 {
    427   struct TALER_BlindedDenominationSignature ds;
    428   enum TALER_ErrorCode ec;
    429   bool success = false;
    430   struct TALER_PlanchetMasterSecretP ps;
    431   struct TALER_CoinSpendPrivateKeyP coin_priv;
    432   union GNUNET_CRYPTO_BlindingSecretP bks;
    433   struct TALER_CoinPubHashP c_hash;
    434   struct GNUNET_CRYPTO_BlindingInputValues bi = {
    435     .cipher = GNUNET_CRYPTO_BSA_CS
    436   };
    437   struct TALER_ExchangeBlindingValues alg_values = {
    438     .blinding_inputs = &bi
    439   };
    440   union GNUNET_CRYPTO_BlindSessionNonce nonce;
    441 
    442   TALER_planchet_master_setup_random (&ps);
    443   for (unsigned int i = 0; i<MAX_KEYS; i++)
    444   {
    445     if (! keys[i].valid)
    446       continue;
    447     {
    448       struct TALER_PlanchetDetail pd;
    449       struct TALER_CRYPTO_CsSignRequest csr;
    450       struct TALER_CRYPTO_CsDeriveRequest cdr = {
    451         .h_cs = &keys[i].h_cs,
    452         .nonce = &nonce.cs_nonce
    453       };
    454 
    455       TALER_cs_withdraw_nonce_derive (&ps,
    456                                       &nonce.cs_nonce);
    457       ec = TALER_CRYPTO_helper_cs_r_derive (
    458         dh,
    459         &cdr,
    460         false,
    461         &bi.details.cs_values);
    462       if (TALER_EC_NONE != ec)
    463         continue;
    464       TALER_planchet_setup_coin_priv (&ps,
    465                                       &alg_values,
    466                                       &coin_priv);
    467       TALER_planchet_blinding_secret_create (&ps,
    468                                              &alg_values,
    469                                              &bks);
    470       GNUNET_assert (GNUNET_YES ==
    471                      TALER_planchet_prepare (&keys[i].denom_pub,
    472                                              &alg_values,
    473                                              &bks,
    474                                              &nonce,
    475                                              &coin_priv,
    476                                              NULL, /* no age commitment */
    477                                              &c_hash,
    478                                              &pd));
    479       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    480                   "Requesting signature with key %s\n",
    481                   GNUNET_h2s (&keys[i].h_cs.hash));
    482       csr.h_cs = &keys[i].h_cs;
    483       csr.blinded_planchet
    484         = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
    485       ec = TALER_CRYPTO_helper_cs_sign (
    486         dh,
    487         &csr,
    488         false,
    489         &ds);
    490       TALER_blinded_planchet_free (&pd.blinded_planchet);
    491     }
    492     switch (ec)
    493     {
    494     case TALER_EC_NONE:
    495       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
    496                                       keys[i].start_time.abs_time),
    497                                     >,
    498                                     GNUNET_TIME_UNIT_SECONDS))
    499       {
    500         /* key worked too early */
    501         GNUNET_break (0);
    502         TALER_blinded_denom_sig_free (&ds);
    503         return 4;
    504       }
    505       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
    506                                       keys[i].start_time.abs_time),
    507                                     >,
    508                                     keys[i].validity_duration))
    509       {
    510         /* key worked too later */
    511         GNUNET_break (0);
    512         TALER_blinded_denom_sig_free (&ds);
    513         return 5;
    514       }
    515       {
    516         struct TALER_FreshCoin coin;
    517 
    518         if (GNUNET_OK !=
    519             TALER_planchet_to_coin (&keys[i].denom_pub,
    520                                     &ds,
    521                                     &bks,
    522                                     &coin_priv,
    523                                     NULL, /* no age commitment */
    524                                     &c_hash,
    525                                     &alg_values,
    526                                     &coin))
    527         {
    528           GNUNET_break (0);
    529           TALER_blinded_denom_sig_free (&ds);
    530           return 6;
    531         }
    532         TALER_blinded_denom_sig_free (&ds);
    533         TALER_denom_sig_free (&coin.sig);
    534       }
    535       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    536                   "Received valid signature for key %s\n",
    537                   GNUNET_h2s (&keys[i].h_cs.hash));
    538       success = true;
    539       break;
    540     case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
    541       /* This 'failure' is expected, we're testing also for the
    542          error handling! */
    543       if ( (GNUNET_TIME_relative_is_zero (
    544               GNUNET_TIME_absolute_get_remaining (
    545                 keys[i].start_time.abs_time))) &&
    546            (GNUNET_TIME_relative_cmp (
    547               GNUNET_TIME_absolute_get_duration (
    548                 keys[i].start_time.abs_time),
    549               <,
    550               keys[i].validity_duration)) )
    551       {
    552         /* key should have worked! */
    553         GNUNET_break (0);
    554         return 6;
    555       }
    556       break;
    557     default:
    558       /* unexpected error */
    559       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    560                   "Unexpected error %d\n",
    561                   ec);
    562       return 7;
    563     }
    564   }
    565   if (! success)
    566   {
    567     /* no valid key for signing found, also bad */
    568     GNUNET_break (0);
    569     return 16;
    570   }
    571 
    572   /* check signing does not work if the key is unknown */
    573   {
    574     struct TALER_PlanchetDetail pd;
    575     struct TALER_CsPubHashP rnd;
    576     struct TALER_CRYPTO_CsSignRequest csr;
    577 
    578     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
    579                                 &rnd,
    580                                 sizeof (rnd));
    581     GNUNET_assert (GNUNET_YES ==
    582                    TALER_planchet_prepare (&keys[0].denom_pub,
    583                                            &alg_values,
    584                                            &bks,
    585                                            &nonce,
    586                                            &coin_priv,
    587                                            NULL, /* no age commitment */
    588                                            &c_hash,
    589                                            &pd));
    590     csr.h_cs = &rnd;
    591     csr.blinded_planchet
    592       = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
    593     ec = TALER_CRYPTO_helper_cs_sign (
    594       dh,
    595       &csr,
    596       false,
    597       &ds);
    598     TALER_blinded_planchet_free (&pd.blinded_planchet);
    599     if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
    600     {
    601       if (TALER_EC_NONE == ec)
    602         TALER_blinded_denom_sig_free (&ds);
    603       GNUNET_break (0);
    604       return 17;
    605     }
    606     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    607                 "Signing with invalid key %s failed as desired\n",
    608                 GNUNET_h2s (&rnd.hash));
    609   }
    610   return 0;
    611 }
    612 
    613 
    614 /**
    615  * Test batch signing logic.
    616  *
    617  * @param dh handle to the helper
    618  * @param batch_size how large should the batch be
    619  * @param check_sigs also check unknown key and signatures
    620  * @return 0 on success
    621  */
    622 static int
    623 test_batch_signing (struct TALER_CRYPTO_CsDenominationHelper *dh,
    624                     unsigned int batch_size,
    625                     bool check_sigs)
    626 {
    627   struct TALER_BlindedDenominationSignature ds[batch_size];
    628   enum TALER_ErrorCode ec;
    629   bool success = false;
    630   struct TALER_PlanchetMasterSecretP ps[batch_size];
    631   struct TALER_CoinSpendPrivateKeyP coin_priv[batch_size];
    632   union GNUNET_CRYPTO_BlindingSecretP bks[batch_size];
    633   struct TALER_CoinPubHashP c_hash[batch_size];
    634   struct GNUNET_CRYPTO_BlindingInputValues bi[batch_size];
    635   struct TALER_ExchangeBlindingValues alg_values[batch_size];
    636   union GNUNET_CRYPTO_BlindSessionNonce nonces[batch_size];
    637 
    638   for (unsigned int i = 0; i<batch_size; i++)
    639     TALER_planchet_master_setup_random (&ps[i]);
    640   for (unsigned int k = 0; k<MAX_KEYS; k++)
    641   {
    642     if (! keys[k].valid)
    643       continue;
    644     {
    645       struct TALER_PlanchetDetail pd[batch_size];
    646       struct TALER_CRYPTO_CsSignRequest csr[batch_size];
    647       struct TALER_CRYPTO_CsDeriveRequest cdr[batch_size];
    648       struct GNUNET_CRYPTO_CSPublicRPairP crps[batch_size];
    649 
    650       for (unsigned int i = 0; i<batch_size; i++)
    651       {
    652         cdr[i].h_cs = &keys[k].h_cs;
    653         cdr[i].nonce = &nonces[i].cs_nonce;
    654         TALER_cs_withdraw_nonce_derive (
    655           &ps[i],
    656           &nonces[i].cs_nonce);
    657         bi[i].cipher = GNUNET_CRYPTO_BSA_CS;
    658         alg_values[i].blinding_inputs = &bi[i];
    659       }
    660       ec = TALER_CRYPTO_helper_cs_r_batch_derive (
    661         dh,
    662         batch_size,
    663         cdr,
    664         false,
    665         crps);
    666       if (TALER_EC_NONE != ec)
    667         continue;
    668       for (unsigned int i = 0; i<batch_size; i++)
    669       {
    670         bi[i].details.cs_values = crps[i];
    671         TALER_planchet_setup_coin_priv (&ps[i],
    672                                         &alg_values[i],
    673                                         &coin_priv[i]);
    674         TALER_planchet_blinding_secret_create (&ps[i],
    675                                                &alg_values[i],
    676                                                &bks[i]);
    677         GNUNET_assert (GNUNET_YES ==
    678                        TALER_planchet_prepare (&keys[k].denom_pub,
    679                                                &alg_values[i],
    680                                                &bks[i],
    681                                                &nonces[i],
    682                                                &coin_priv[i],
    683                                                NULL, /* no age commitment */
    684                                                &c_hash[i],
    685                                                &pd[i]));
    686         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    687                     "Requesting signature with key %s\n",
    688                     GNUNET_h2s (&keys[k].h_cs.hash));
    689         csr[i].h_cs = &keys[k].h_cs;
    690         csr[i].blinded_planchet
    691           = &pd[i].blinded_planchet.blinded_message->details.cs_blinded_message;
    692       }
    693       ec = TALER_CRYPTO_helper_cs_batch_sign (
    694         dh,
    695         batch_size,
    696         csr,
    697         false,
    698         ds);
    699       for (unsigned int i = 0; i<batch_size; i++)
    700       {
    701         TALER_blinded_planchet_free (&pd[i].blinded_planchet);
    702       }
    703     }
    704     switch (ec)
    705     {
    706     case TALER_EC_NONE:
    707       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
    708                                       keys[k].start_time.abs_time),
    709                                     >,
    710                                     GNUNET_TIME_UNIT_SECONDS))
    711       {
    712         /* key worked too early */
    713         GNUNET_break (0);
    714         return 4;
    715       }
    716       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
    717                                       keys[k].start_time.abs_time),
    718                                     >,
    719                                     keys[k].validity_duration))
    720       {
    721         /* key worked too later */
    722         GNUNET_break (0);
    723         return 5;
    724       }
    725       if (check_sigs)
    726       {
    727         for (unsigned int i = 0; i<batch_size; i++)
    728         {
    729           struct TALER_FreshCoin coin;
    730 
    731           if (GNUNET_OK !=
    732               TALER_planchet_to_coin (&keys[k].denom_pub,
    733                                       &ds[i],
    734                                       &bks[i],
    735                                       &coin_priv[i],
    736                                       NULL, /* no age commitment */
    737                                       &c_hash[i],
    738                                       &alg_values[i],
    739                                       &coin))
    740           {
    741             GNUNET_break (0);
    742             return 6;
    743           }
    744           TALER_blinded_denom_sig_free (&ds[i]);
    745           TALER_denom_sig_free (&coin.sig);
    746         }
    747         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    748                     "Received valid signature for key %s\n",
    749                     GNUNET_h2s (&keys[k].h_cs.hash));
    750       }
    751       else
    752       {
    753         for (unsigned int i = 0; i<batch_size; i++)
    754           TALER_blinded_denom_sig_free (&ds[i]);
    755       }
    756       success = true;
    757       break;
    758     case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
    759       /* This 'failure' is expected, we're testing also for the
    760          error handling! */
    761       if ( (GNUNET_TIME_relative_is_zero (
    762               GNUNET_TIME_absolute_get_remaining (
    763                 keys[k].start_time.abs_time))) &&
    764            (GNUNET_TIME_relative_cmp (
    765               GNUNET_TIME_absolute_get_duration (
    766                 keys[k].start_time.abs_time),
    767               <,
    768               keys[k].validity_duration)) )
    769       {
    770         /* key should have worked! */
    771         GNUNET_break (0);
    772         return 6;
    773       }
    774       break;
    775     default:
    776       /* unexpected error */
    777       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    778                   "Unexpected error %d\n",
    779                   ec);
    780       return 7;
    781     }
    782   }
    783   if (! success)
    784   {
    785     /* no valid key for signing found, also bad */
    786     GNUNET_break (0);
    787     return 16;
    788   }
    789 
    790   /* check signing does not work if the key is unknown */
    791   if (check_sigs)
    792   {
    793     struct TALER_PlanchetDetail pd;
    794     struct TALER_CsPubHashP rnd;
    795     struct TALER_CRYPTO_CsSignRequest csr;
    796 
    797     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
    798                                 &rnd,
    799                                 sizeof (rnd));
    800     GNUNET_assert (GNUNET_YES ==
    801                    TALER_planchet_prepare (&keys[0].denom_pub,
    802                                            &alg_values[0],
    803                                            &bks[0],
    804                                            &nonces[0],
    805                                            &coin_priv[0],
    806                                            NULL, /* no age commitment */
    807                                            &c_hash[0],
    808                                            &pd));
    809     csr.h_cs = &rnd;
    810     csr.blinded_planchet
    811       = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
    812     ec = TALER_CRYPTO_helper_cs_batch_sign (
    813       dh,
    814       1,
    815       &csr,
    816       false,
    817       &ds[0]);
    818     TALER_blinded_planchet_free (&pd.blinded_planchet);
    819     if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
    820     {
    821       if (TALER_EC_NONE == ec)
    822         TALER_blinded_denom_sig_free (&ds[0]);
    823       GNUNET_break (0);
    824       return 17;
    825     }
    826     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    827                 "Signing with invalid key %s failed as desired\n",
    828                 GNUNET_h2s (&rnd.hash));
    829   }
    830   return 0;
    831 }
    832 
    833 
    834 /**
    835  * Benchmark signing logic.
    836  *
    837  * @param dh handle to the helper
    838  * @return 0 on success
    839  */
    840 static int
    841 perf_signing (struct TALER_CRYPTO_CsDenominationHelper *dh,
    842               const char *type)
    843 {
    844   struct TALER_BlindedDenominationSignature ds;
    845   enum TALER_ErrorCode ec;
    846   struct GNUNET_TIME_Relative duration;
    847   struct TALER_PlanchetMasterSecretP ps;
    848   struct TALER_CoinSpendPrivateKeyP coin_priv;
    849   union GNUNET_CRYPTO_BlindingSecretP bks;
    850   struct GNUNET_CRYPTO_BlindingInputValues bv = {
    851     .cipher = GNUNET_CRYPTO_BSA_CS
    852   };
    853   struct TALER_ExchangeBlindingValues alg_values = {
    854     .blinding_inputs = &bv
    855   };
    856 
    857   TALER_planchet_master_setup_random (&ps);
    858   duration = GNUNET_TIME_UNIT_ZERO;
    859   TALER_CRYPTO_helper_cs_poll (dh);
    860   for (unsigned int j = 0; j<NUM_SIGN_PERFS;)
    861   {
    862     for (unsigned int i = 0; i<MAX_KEYS; i++)
    863     {
    864       if (! keys[i].valid)
    865         continue;
    866       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
    867                                       keys[i].start_time.abs_time),
    868                                     >,
    869                                     GNUNET_TIME_UNIT_SECONDS))
    870         continue;
    871       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
    872                                       keys[i].start_time.abs_time),
    873                                     >,
    874                                     keys[i].validity_duration))
    875         continue;
    876       {
    877         struct TALER_CoinPubHashP c_hash;
    878         struct TALER_PlanchetDetail pd;
    879         union GNUNET_CRYPTO_BlindSessionNonce nonce;
    880         struct TALER_CRYPTO_CsDeriveRequest cdr = {
    881           .h_cs = &keys[i].h_cs,
    882           .nonce = &nonce.cs_nonce
    883         };
    884 
    885         TALER_cs_withdraw_nonce_derive (
    886           &ps,
    887           &nonce.cs_nonce);
    888         ec = TALER_CRYPTO_helper_cs_r_derive (
    889           dh,
    890           &cdr,
    891           true,
    892           &bv.details.cs_values);
    893         if (TALER_EC_NONE != ec)
    894           continue;
    895         TALER_planchet_setup_coin_priv (&ps,
    896                                         &alg_values,
    897                                         &coin_priv);
    898         TALER_planchet_blinding_secret_create (&ps,
    899                                                &alg_values,
    900                                                &bks);
    901         GNUNET_assert (GNUNET_YES ==
    902                        TALER_planchet_prepare (&keys[i].denom_pub,
    903                                                &alg_values,
    904                                                &bks,
    905                                                &nonce,
    906                                                &coin_priv,
    907                                                NULL, /* no age commitment */
    908                                                &c_hash,
    909                                                &pd));
    910         /* use this key as long as it works */
    911         while (1)
    912         {
    913           struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get ();
    914           struct GNUNET_TIME_Relative delay;
    915           struct TALER_CRYPTO_CsSignRequest csr;
    916 
    917           csr.h_cs = &keys[i].h_cs;
    918           csr.blinded_planchet
    919             = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
    920           ec = TALER_CRYPTO_helper_cs_sign (
    921             dh,
    922             &csr,
    923             true,
    924             &ds);
    925           if (TALER_EC_NONE != ec)
    926             break;
    927           delay = GNUNET_TIME_absolute_get_duration (start);
    928           duration = GNUNET_TIME_relative_add (duration,
    929                                                delay);
    930           TALER_blinded_denom_sig_free (&ds);
    931           j++;
    932           if (NUM_SIGN_PERFS <= j)
    933             break;
    934         }
    935         TALER_blinded_planchet_free (&pd.blinded_planchet);
    936       }
    937     }   /* for i */
    938   }   /* for j */
    939   fprintf (stderr,
    940            "%u (%s) signature operations took %s\n",
    941            (unsigned int) NUM_SIGN_PERFS,
    942            type,
    943            GNUNET_STRINGS_relative_time_to_string (duration,
    944                                                    GNUNET_YES));
    945   return 0;
    946 }
    947 
    948 
    949 /**
    950  * Parallel signing logic.
    951  *
    952  * @param esh handle to the helper
    953  * @return 0 on success
    954  */
    955 static int
    956 par_signing (struct GNUNET_CONFIGURATION_Handle *cfg)
    957 {
    958   struct GNUNET_TIME_Absolute start;
    959   struct GNUNET_TIME_Relative duration;
    960   pid_t pids[NUM_CORES];
    961   struct TALER_CRYPTO_CsDenominationHelper *dh;
    962 
    963   start = GNUNET_TIME_absolute_get ();
    964   for (unsigned int i = 0; i<NUM_CORES; i++)
    965   {
    966     pids[i] = fork ();
    967     num_keys = 0;
    968     GNUNET_assert (-1 != pids[i]);
    969     if (0 == pids[i])
    970     {
    971       int ret;
    972 
    973       dh = TALER_CRYPTO_helper_cs_connect (cfg,
    974                                            "taler-exchange",
    975                                            &key_cb,
    976                                            NULL);
    977       GNUNET_assert (NULL != dh);
    978       ret = perf_signing (dh,
    979                           "parallel");
    980       TALER_CRYPTO_helper_cs_disconnect (dh);
    981       free_keys ();
    982       exit (ret);
    983     }
    984   }
    985   for (unsigned int i = 0; i<NUM_CORES; i++)
    986   {
    987     int wstatus;
    988 
    989     GNUNET_assert (pids[i] ==
    990                    waitpid (pids[i],
    991                             &wstatus,
    992                             0));
    993   }
    994   duration = GNUNET_TIME_absolute_get_duration (start);
    995   fprintf (stderr,
    996            "%u (parallel) signature operations took %s (total real time)\n",
    997            (unsigned int) NUM_SIGN_PERFS * NUM_CORES,
    998            GNUNET_STRINGS_relative_time_to_string (duration,
    999                                                    GNUNET_YES));
   1000   return 0;
   1001 }
   1002 
   1003 
   1004 /**
   1005  * Main entry point into the test logic with the helper already running.
   1006  */
   1007 static int
   1008 run_test (void)
   1009 {
   1010   struct GNUNET_CONFIGURATION_Handle *cfg;
   1011   struct TALER_CRYPTO_CsDenominationHelper *dh;
   1012   struct timespec req = {
   1013     .tv_nsec = 250000000
   1014   };
   1015   int ret;
   1016 
   1017   cfg = GNUNET_CONFIGURATION_create (TALER_EXCHANGE_project_data ());
   1018   if (GNUNET_OK !=
   1019       GNUNET_CONFIGURATION_load (cfg,
   1020                                  "test_helper_cs.conf"))
   1021   {
   1022     GNUNET_break (0);
   1023     return 77;
   1024   }
   1025 
   1026   fprintf (stderr, "Waiting for helper to start ... ");
   1027   for (unsigned int i = 0; i<100; i++)
   1028   {
   1029     nanosleep (&req,
   1030                NULL);
   1031     dh = TALER_CRYPTO_helper_cs_connect (cfg,
   1032                                          "taler-exchange",
   1033                                          &key_cb,
   1034                                          NULL);
   1035     if (NULL != dh)
   1036       break;
   1037     fprintf (stderr, ".");
   1038   }
   1039   if (NULL == dh)
   1040   {
   1041     fprintf (stderr,
   1042              "\nFAILED: timeout trying to connect to helper\n");
   1043     GNUNET_CONFIGURATION_destroy (cfg);
   1044     return 1;
   1045   }
   1046   if (0 == num_keys)
   1047   {
   1048     fprintf (stderr,
   1049              "\nFAILED: timeout trying to connect to helper\n");
   1050     TALER_CRYPTO_helper_cs_disconnect (dh);
   1051     GNUNET_CONFIGURATION_destroy (cfg);
   1052     return 1;
   1053   }
   1054   fprintf (stderr,
   1055            " Done (%u keys)\n",
   1056            num_keys);
   1057   ret = 0;
   1058   if (0 == ret)
   1059     ret = test_revocation (dh);
   1060   if (0 == ret)
   1061     ret = test_r_derive (dh);
   1062   if (0 == ret)
   1063     ret = test_signing (dh);
   1064   if (0 == ret)
   1065     ret = test_batch_signing (dh,
   1066                               2,
   1067                               true);
   1068   if (0 == ret)
   1069     ret = test_batch_signing (dh,
   1070                               64,
   1071                               true);
   1072   for (unsigned int i = 0; i<4; i++)
   1073   {
   1074     static unsigned int batches[] = { 1, 4, 16, 64 };
   1075     unsigned int batch_size = batches[i];
   1076     struct GNUNET_TIME_Absolute start;
   1077     struct GNUNET_TIME_Relative duration;
   1078 
   1079     start = GNUNET_TIME_absolute_get ();
   1080     if (0 != ret)
   1081       break;
   1082     ret = test_batch_signing (dh,
   1083                               batch_size,
   1084                               false);
   1085     duration = GNUNET_TIME_absolute_get_duration (start);
   1086     fprintf (stderr,
   1087              "%4u (batch) signature operations took %s (total real time)\n",
   1088              (unsigned int) batch_size,
   1089              GNUNET_STRINGS_relative_time_to_string (duration,
   1090                                                      GNUNET_YES));
   1091   }
   1092   if (0 == ret)
   1093     ret = perf_signing (dh,
   1094                         "sequential");
   1095   TALER_CRYPTO_helper_cs_disconnect (dh);
   1096   free_keys ();
   1097   if (0 == ret)
   1098     ret = par_signing (cfg);
   1099   /* clean up our state */
   1100   GNUNET_CONFIGURATION_destroy (cfg);
   1101   return ret;
   1102 }
   1103 
   1104 
   1105 int
   1106 main (int argc,
   1107       const char *const argv[])
   1108 {
   1109   struct GNUNET_OS_Process *helper;
   1110   char *libexec_dir;
   1111   char *binary_name;
   1112   int ret;
   1113   enum GNUNET_OS_ProcessStatusType type;
   1114   unsigned long code;
   1115   const char *loglev = "WARNING";
   1116 
   1117   (void) argc;
   1118   (void) argv;
   1119   unsetenv ("XDG_DATA_HOME");
   1120   unsetenv ("XDG_CONFIG_HOME");
   1121   GNUNET_log_setup ("test-helper-cs",
   1122                     loglev,
   1123                     NULL);
   1124   libexec_dir = GNUNET_OS_installation_get_path (TALER_EXCHANGE_project_data (),
   1125                                                  GNUNET_OS_IPK_BINDIR);
   1126   GNUNET_asprintf (&binary_name,
   1127                    "%s/%s",
   1128                    libexec_dir,
   1129                    "taler-exchange-secmod-cs");
   1130   GNUNET_free (libexec_dir);
   1131   helper = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
   1132                                     NULL, NULL, NULL,
   1133                                     binary_name,
   1134                                     binary_name,
   1135                                     "-c",
   1136                                     "test_helper_cs.conf",
   1137                                     "-L",
   1138                                     loglev,
   1139                                     NULL);
   1140   if (NULL == helper)
   1141   {
   1142     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
   1143                               "exec",
   1144                               binary_name);
   1145     GNUNET_free (binary_name);
   1146     return 77;
   1147   }
   1148   GNUNET_free (binary_name);
   1149   ret = run_test ();
   1150 
   1151   GNUNET_OS_process_kill (helper,
   1152                           SIGTERM);
   1153   if (GNUNET_OK !=
   1154       GNUNET_OS_process_wait_status (helper,
   1155                                      &type,
   1156                                      &code))
   1157   {
   1158     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1159                 "Helper process did not die voluntarily, killing hard\n");
   1160     GNUNET_OS_process_kill (helper,
   1161                             SIGKILL);
   1162     ret = 4;
   1163   }
   1164   else if ( (GNUNET_OS_PROCESS_EXITED != type) ||
   1165             (0 != code) )
   1166   {
   1167     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1168                 "Helper died with unexpected status %d/%d\n",
   1169                 (int) type,
   1170                 (int) code);
   1171     ret = 5;
   1172   }
   1173   GNUNET_OS_process_destroy (helper);
   1174   return ret;
   1175 }
   1176 
   1177 
   1178 /* end of test_helper_cs.c */