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 (36920B)


      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 "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_batch_derive (
    304         dh,
    305         1,
    306         &cdr,
    307         false,
    308         &bi.details.cs_values);
    309     }
    310     switch (ec)
    311     {
    312     case TALER_EC_NONE:
    313       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
    314                                       keys[i].start_time.abs_time),
    315                                     >,
    316                                     GNUNET_TIME_UNIT_SECONDS))
    317       {
    318         /* key worked too early */
    319         GNUNET_break (0);
    320         return 4;
    321       }
    322       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
    323                                       keys[i].start_time.abs_time),
    324                                     >,
    325                                     keys[i].validity_duration))
    326       {
    327         /* key worked too later */
    328         GNUNET_break (0);
    329         return 5;
    330       }
    331 
    332       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    333                   "Received valid R for key %s\n",
    334                   GNUNET_h2s (&keys[i].h_cs.hash));
    335       TALER_planchet_setup_coin_priv (&ps,
    336                                       &alg_values,
    337                                       &coin_priv);
    338       TALER_planchet_blinding_secret_create (&ps,
    339                                              &alg_values,
    340                                              &bks);
    341       GNUNET_assert (GNUNET_OK ==
    342                      TALER_planchet_prepare (&keys[i].denom_pub,
    343                                              &alg_values,
    344                                              &bks,
    345                                              &nonce,
    346                                              &coin_priv,
    347                                              NULL, /* no age commitment */
    348                                              &c_hash,
    349                                              &pd));
    350       TALER_blinded_planchet_free (&pd.blinded_planchet);
    351       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    352                   "Successfully prepared planchet");
    353       success = true;
    354       break;
    355     case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
    356       /* This 'failure' is expected, we're testing also for the
    357          error handling! */
    358       if ( (GNUNET_TIME_relative_is_zero (
    359               GNUNET_TIME_absolute_get_remaining (
    360                 keys[i].start_time.abs_time))) &&
    361            (GNUNET_TIME_relative_cmp (
    362               GNUNET_TIME_absolute_get_duration (
    363                 keys[i].start_time.abs_time),
    364               <,
    365               keys[i].validity_duration)) )
    366       {
    367         /* key should have worked! */
    368         GNUNET_break (0);
    369         return 6;
    370       }
    371       break;
    372     default:
    373       /* unexpected error */
    374       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    375                   "Unexpected error %d\n",
    376                   ec);
    377       return 7;
    378     }
    379   }
    380   if (! success)
    381   {
    382     /* no valid key for signing found, also bad */
    383     GNUNET_break (0);
    384     return 16;
    385   }
    386 
    387   /* check R derivation does not work if the key is unknown */
    388   {
    389     struct TALER_CsPubHashP rnd;
    390     struct GNUNET_CRYPTO_CSPublicRPairP crp;
    391     struct TALER_CRYPTO_CsDeriveRequest cdr = {
    392       .h_cs = &rnd,
    393       .nonce = &nonce.cs_nonce,
    394     };
    395 
    396     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
    397                                 &rnd,
    398                                 sizeof (rnd));
    399     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
    400                                 &nonce,
    401                                 sizeof (nonce));
    402     ec = TALER_CRYPTO_helper_cs_r_batch_derive (dh,
    403                                                 1,
    404                                                 &cdr,
    405                                                 false,
    406                                                 &crp);
    407     if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
    408     {
    409       GNUNET_break (0);
    410       return 17;
    411     }
    412     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    413                 "R derivation with invalid key %s failed as desired\n",
    414                 GNUNET_h2s (&rnd.hash));
    415   }
    416   return 0;
    417 }
    418 
    419 
    420 /**
    421  * Test signing logic.
    422  *
    423  * @param dh handle to the helper
    424  * @return 0 on success
    425  */
    426 static int
    427 test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh)
    428 {
    429   struct TALER_BlindedDenominationSignature ds;
    430   enum TALER_ErrorCode ec;
    431   bool success = false;
    432   struct TALER_PlanchetMasterSecretP ps;
    433   struct TALER_CoinSpendPrivateKeyP coin_priv;
    434   union GNUNET_CRYPTO_BlindingSecretP bks;
    435   struct TALER_CoinPubHashP c_hash;
    436   struct GNUNET_CRYPTO_BlindingInputValues bi = {
    437     .cipher = GNUNET_CRYPTO_BSA_CS
    438   };
    439   struct TALER_ExchangeBlindingValues alg_values = {
    440     .blinding_inputs = &bi
    441   };
    442   union GNUNET_CRYPTO_BlindSessionNonce nonce;
    443 
    444   TALER_planchet_master_setup_random (&ps);
    445   for (unsigned int i = 0; i<MAX_KEYS; i++)
    446   {
    447     if (! keys[i].valid)
    448       continue;
    449     {
    450       struct TALER_PlanchetDetail pd;
    451       struct TALER_CRYPTO_CsSignRequest csr;
    452       struct TALER_CRYPTO_CsDeriveRequest cdr = {
    453         .h_cs = &keys[i].h_cs,
    454         .nonce = &nonce.cs_nonce
    455       };
    456 
    457       TALER_cs_withdraw_nonce_derive (&ps,
    458                                       &nonce.cs_nonce);
    459       ec = TALER_CRYPTO_helper_cs_r_batch_derive (
    460         dh,
    461         1,
    462         &cdr,
    463         false,
    464         &bi.details.cs_values);
    465       if (TALER_EC_NONE != ec)
    466         continue;
    467       TALER_planchet_setup_coin_priv (&ps,
    468                                       &alg_values,
    469                                       &coin_priv);
    470       TALER_planchet_blinding_secret_create (&ps,
    471                                              &alg_values,
    472                                              &bks);
    473       GNUNET_assert (GNUNET_YES ==
    474                      TALER_planchet_prepare (&keys[i].denom_pub,
    475                                              &alg_values,
    476                                              &bks,
    477                                              &nonce,
    478                                              &coin_priv,
    479                                              NULL, /* no age commitment */
    480                                              &c_hash,
    481                                              &pd));
    482       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    483                   "Requesting signature with key %s\n",
    484                   GNUNET_h2s (&keys[i].h_cs.hash));
    485       csr.h_cs = &keys[i].h_cs;
    486       csr.blinded_planchet
    487         = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
    488       ec = TALER_CRYPTO_helper_cs_batch_sign (
    489         dh,
    490         1,
    491         &csr,
    492         false,
    493         &ds);
    494       TALER_blinded_planchet_free (&pd.blinded_planchet);
    495     }
    496     switch (ec)
    497     {
    498     case TALER_EC_NONE:
    499       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
    500                                       keys[i].start_time.abs_time),
    501                                     >,
    502                                     GNUNET_TIME_UNIT_SECONDS))
    503       {
    504         /* key worked too early */
    505         GNUNET_break (0);
    506         TALER_blinded_denom_sig_free (&ds);
    507         return 4;
    508       }
    509       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
    510                                       keys[i].start_time.abs_time),
    511                                     >,
    512                                     keys[i].validity_duration))
    513       {
    514         /* key worked too later */
    515         GNUNET_break (0);
    516         TALER_blinded_denom_sig_free (&ds);
    517         return 5;
    518       }
    519       {
    520         struct TALER_FreshCoin coin;
    521 
    522         if (GNUNET_OK !=
    523             TALER_planchet_to_coin (&keys[i].denom_pub,
    524                                     &ds,
    525                                     &bks,
    526                                     &coin_priv,
    527                                     NULL, /* no age commitment */
    528                                     &c_hash,
    529                                     &alg_values,
    530                                     &coin))
    531         {
    532           GNUNET_break (0);
    533           TALER_blinded_denom_sig_free (&ds);
    534           return 6;
    535         }
    536         TALER_blinded_denom_sig_free (&ds);
    537         TALER_denom_sig_free (&coin.sig);
    538       }
    539       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    540                   "Received valid signature for key %s\n",
    541                   GNUNET_h2s (&keys[i].h_cs.hash));
    542       success = true;
    543       break;
    544     case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
    545       /* This 'failure' is expected, we're testing also for the
    546          error handling! */
    547       if ( (GNUNET_TIME_relative_is_zero (
    548               GNUNET_TIME_absolute_get_remaining (
    549                 keys[i].start_time.abs_time))) &&
    550            (GNUNET_TIME_relative_cmp (
    551               GNUNET_TIME_absolute_get_duration (
    552                 keys[i].start_time.abs_time),
    553               <,
    554               keys[i].validity_duration)) )
    555       {
    556         /* key should have worked! */
    557         GNUNET_break (0);
    558         return 6;
    559       }
    560       break;
    561     default:
    562       /* unexpected error */
    563       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    564                   "Unexpected error %d\n",
    565                   ec);
    566       return 7;
    567     }
    568   }
    569   if (! success)
    570   {
    571     /* no valid key for signing found, also bad */
    572     GNUNET_break (0);
    573     return 16;
    574   }
    575 
    576   /* check signing does not work if the key is unknown */
    577   {
    578     struct TALER_PlanchetDetail pd;
    579     struct TALER_CsPubHashP rnd;
    580     struct TALER_CRYPTO_CsSignRequest csr;
    581 
    582     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
    583                                 &rnd,
    584                                 sizeof (rnd));
    585     GNUNET_assert (GNUNET_YES ==
    586                    TALER_planchet_prepare (&keys[0].denom_pub,
    587                                            &alg_values,
    588                                            &bks,
    589                                            &nonce,
    590                                            &coin_priv,
    591                                            NULL, /* no age commitment */
    592                                            &c_hash,
    593                                            &pd));
    594     csr.h_cs = &rnd;
    595     csr.blinded_planchet
    596       = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
    597     ec = TALER_CRYPTO_helper_cs_batch_sign (
    598       dh,
    599       1,
    600       &csr,
    601       false,
    602       &ds);
    603     TALER_blinded_planchet_free (&pd.blinded_planchet);
    604     if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
    605     {
    606       if (TALER_EC_NONE == ec)
    607         TALER_blinded_denom_sig_free (&ds);
    608       GNUNET_break (0);
    609       return 17;
    610     }
    611     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    612                 "Signing with invalid key %s failed as desired\n",
    613                 GNUNET_h2s (&rnd.hash));
    614   }
    615   return 0;
    616 }
    617 
    618 
    619 /**
    620  * Test batch signing logic.
    621  *
    622  * @param dh handle to the helper
    623  * @param batch_size how large should the batch be
    624  * @param check_sigs also check unknown key and signatures
    625  * @return 0 on success
    626  */
    627 static int
    628 test_batch_signing (struct TALER_CRYPTO_CsDenominationHelper *dh,
    629                     unsigned int batch_size,
    630                     bool check_sigs)
    631 {
    632   struct TALER_BlindedDenominationSignature ds[batch_size];
    633   enum TALER_ErrorCode ec;
    634   bool success = false;
    635   struct TALER_PlanchetMasterSecretP ps[batch_size];
    636   struct TALER_CoinSpendPrivateKeyP coin_priv[batch_size];
    637   union GNUNET_CRYPTO_BlindingSecretP bks[batch_size];
    638   struct TALER_CoinPubHashP c_hash[batch_size];
    639   struct GNUNET_CRYPTO_BlindingInputValues bi[batch_size];
    640   struct TALER_ExchangeBlindingValues alg_values[batch_size];
    641   union GNUNET_CRYPTO_BlindSessionNonce nonces[batch_size];
    642 
    643   for (unsigned int i = 0; i<batch_size; i++)
    644     TALER_planchet_master_setup_random (&ps[i]);
    645   for (unsigned int k = 0; k<MAX_KEYS; k++)
    646   {
    647     if (! keys[k].valid)
    648       continue;
    649     {
    650       struct TALER_PlanchetDetail pd[batch_size];
    651       struct TALER_CRYPTO_CsSignRequest csr[batch_size];
    652       struct TALER_CRYPTO_CsDeriveRequest cdr[batch_size];
    653       struct GNUNET_CRYPTO_CSPublicRPairP crps[batch_size];
    654 
    655       for (unsigned int i = 0; i<batch_size; i++)
    656       {
    657         cdr[i].h_cs = &keys[k].h_cs;
    658         cdr[i].nonce = &nonces[i].cs_nonce;
    659         TALER_cs_withdraw_nonce_derive (
    660           &ps[i],
    661           &nonces[i].cs_nonce);
    662         bi[i].cipher = GNUNET_CRYPTO_BSA_CS;
    663         alg_values[i].blinding_inputs = &bi[i];
    664       }
    665       ec = TALER_CRYPTO_helper_cs_r_batch_derive (
    666         dh,
    667         batch_size,
    668         cdr,
    669         false,
    670         crps);
    671       if (TALER_EC_NONE != ec)
    672         continue;
    673       for (unsigned int i = 0; i<batch_size; i++)
    674       {
    675         bi[i].details.cs_values = crps[i];
    676         TALER_planchet_setup_coin_priv (&ps[i],
    677                                         &alg_values[i],
    678                                         &coin_priv[i]);
    679         TALER_planchet_blinding_secret_create (&ps[i],
    680                                                &alg_values[i],
    681                                                &bks[i]);
    682         GNUNET_assert (GNUNET_YES ==
    683                        TALER_planchet_prepare (&keys[k].denom_pub,
    684                                                &alg_values[i],
    685                                                &bks[i],
    686                                                &nonces[i],
    687                                                &coin_priv[i],
    688                                                NULL, /* no age commitment */
    689                                                &c_hash[i],
    690                                                &pd[i]));
    691         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    692                     "Requesting signature with key %s\n",
    693                     GNUNET_h2s (&keys[k].h_cs.hash));
    694         csr[i].h_cs = &keys[k].h_cs;
    695         csr[i].blinded_planchet
    696           = &pd[i].blinded_planchet.blinded_message->details.cs_blinded_message;
    697       }
    698       ec = TALER_CRYPTO_helper_cs_batch_sign (
    699         dh,
    700         batch_size,
    701         csr,
    702         false,
    703         ds);
    704       for (unsigned int i = 0; i<batch_size; i++)
    705       {
    706         TALER_blinded_planchet_free (&pd[i].blinded_planchet);
    707       }
    708     }
    709     switch (ec)
    710     {
    711     case TALER_EC_NONE:
    712       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
    713                                       keys[k].start_time.abs_time),
    714                                     >,
    715                                     GNUNET_TIME_UNIT_SECONDS))
    716       {
    717         /* key worked too early */
    718         GNUNET_break (0);
    719         return 4;
    720       }
    721       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
    722                                       keys[k].start_time.abs_time),
    723                                     >,
    724                                     keys[k].validity_duration))
    725       {
    726         /* key worked too later */
    727         GNUNET_break (0);
    728         return 5;
    729       }
    730       if (check_sigs)
    731       {
    732         for (unsigned int i = 0; i<batch_size; i++)
    733         {
    734           struct TALER_FreshCoin coin;
    735 
    736           if (GNUNET_OK !=
    737               TALER_planchet_to_coin (&keys[k].denom_pub,
    738                                       &ds[i],
    739                                       &bks[i],
    740                                       &coin_priv[i],
    741                                       NULL, /* no age commitment */
    742                                       &c_hash[i],
    743                                       &alg_values[i],
    744                                       &coin))
    745           {
    746             GNUNET_break (0);
    747             return 6;
    748           }
    749           TALER_blinded_denom_sig_free (&ds[i]);
    750           TALER_denom_sig_free (&coin.sig);
    751         }
    752         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    753                     "Received valid signature for key %s\n",
    754                     GNUNET_h2s (&keys[k].h_cs.hash));
    755       }
    756       else
    757       {
    758         for (unsigned int i = 0; i<batch_size; i++)
    759           TALER_blinded_denom_sig_free (&ds[i]);
    760       }
    761       success = true;
    762       break;
    763     case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
    764       /* This 'failure' is expected, we're testing also for the
    765          error handling! */
    766       if ( (GNUNET_TIME_relative_is_zero (
    767               GNUNET_TIME_absolute_get_remaining (
    768                 keys[k].start_time.abs_time))) &&
    769            (GNUNET_TIME_relative_cmp (
    770               GNUNET_TIME_absolute_get_duration (
    771                 keys[k].start_time.abs_time),
    772               <,
    773               keys[k].validity_duration)) )
    774       {
    775         /* key should have worked! */
    776         GNUNET_break (0);
    777         return 6;
    778       }
    779       break;
    780     default:
    781       /* unexpected error */
    782       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    783                   "Unexpected error %d\n",
    784                   ec);
    785       return 7;
    786     }
    787   }
    788   if (! success)
    789   {
    790     /* no valid key for signing found, also bad */
    791     GNUNET_break (0);
    792     return 16;
    793   }
    794 
    795   /* check signing does not work if the key is unknown */
    796   if (check_sigs)
    797   {
    798     struct TALER_PlanchetDetail pd;
    799     struct TALER_CsPubHashP rnd;
    800     struct TALER_CRYPTO_CsSignRequest csr;
    801 
    802     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
    803                                 &rnd,
    804                                 sizeof (rnd));
    805     GNUNET_assert (GNUNET_YES ==
    806                    TALER_planchet_prepare (&keys[0].denom_pub,
    807                                            &alg_values[0],
    808                                            &bks[0],
    809                                            &nonces[0],
    810                                            &coin_priv[0],
    811                                            NULL, /* no age commitment */
    812                                            &c_hash[0],
    813                                            &pd));
    814     csr.h_cs = &rnd;
    815     csr.blinded_planchet
    816       = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
    817     ec = TALER_CRYPTO_helper_cs_batch_sign (
    818       dh,
    819       1,
    820       &csr,
    821       false,
    822       &ds[0]);
    823     TALER_blinded_planchet_free (&pd.blinded_planchet);
    824     if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
    825     {
    826       if (TALER_EC_NONE == ec)
    827         TALER_blinded_denom_sig_free (&ds[0]);
    828       GNUNET_break (0);
    829       return 17;
    830     }
    831     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    832                 "Signing with invalid key %s failed as desired\n",
    833                 GNUNET_h2s (&rnd.hash));
    834   }
    835   return 0;
    836 }
    837 
    838 
    839 /**
    840  * Benchmark signing logic.
    841  *
    842  * @param dh handle to the helper
    843  * @return 0 on success
    844  */
    845 static int
    846 perf_signing (struct TALER_CRYPTO_CsDenominationHelper *dh,
    847               const char *type)
    848 {
    849   struct TALER_BlindedDenominationSignature ds;
    850   enum TALER_ErrorCode ec;
    851   struct GNUNET_TIME_Relative duration;
    852   struct TALER_PlanchetMasterSecretP ps;
    853   struct TALER_CoinSpendPrivateKeyP coin_priv;
    854   union GNUNET_CRYPTO_BlindingSecretP bks;
    855   struct GNUNET_CRYPTO_BlindingInputValues bv = {
    856     .cipher = GNUNET_CRYPTO_BSA_CS
    857   };
    858   struct TALER_ExchangeBlindingValues alg_values = {
    859     .blinding_inputs = &bv
    860   };
    861 
    862   TALER_planchet_master_setup_random (&ps);
    863   duration = GNUNET_TIME_UNIT_ZERO;
    864   TALER_CRYPTO_helper_cs_poll (dh);
    865   for (unsigned int j = 0; j<NUM_SIGN_PERFS;)
    866   {
    867     for (unsigned int i = 0; i<MAX_KEYS; i++)
    868     {
    869       if (! keys[i].valid)
    870         continue;
    871       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining (
    872                                       keys[i].start_time.abs_time),
    873                                     >,
    874                                     GNUNET_TIME_UNIT_SECONDS))
    875         continue;
    876       if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration (
    877                                       keys[i].start_time.abs_time),
    878                                     >,
    879                                     keys[i].validity_duration))
    880         continue;
    881       {
    882         struct TALER_CoinPubHashP c_hash;
    883         struct TALER_PlanchetDetail pd;
    884         union GNUNET_CRYPTO_BlindSessionNonce nonce;
    885         struct TALER_CRYPTO_CsDeriveRequest cdr = {
    886           .h_cs = &keys[i].h_cs,
    887           .nonce = &nonce.cs_nonce
    888         };
    889 
    890         TALER_cs_withdraw_nonce_derive (
    891           &ps,
    892           &nonce.cs_nonce);
    893         ec = TALER_CRYPTO_helper_cs_r_batch_derive (
    894           dh,
    895           1,
    896           &cdr,
    897           true,
    898           &bv.details.cs_values);
    899         if (TALER_EC_NONE != ec)
    900           continue;
    901         TALER_planchet_setup_coin_priv (&ps,
    902                                         &alg_values,
    903                                         &coin_priv);
    904         TALER_planchet_blinding_secret_create (&ps,
    905                                                &alg_values,
    906                                                &bks);
    907         GNUNET_assert (GNUNET_YES ==
    908                        TALER_planchet_prepare (&keys[i].denom_pub,
    909                                                &alg_values,
    910                                                &bks,
    911                                                &nonce,
    912                                                &coin_priv,
    913                                                NULL, /* no age commitment */
    914                                                &c_hash,
    915                                                &pd));
    916         /* use this key as long as it works */
    917         while (1)
    918         {
    919           struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get ();
    920           struct GNUNET_TIME_Relative delay;
    921           struct TALER_CRYPTO_CsSignRequest csr;
    922 
    923           csr.h_cs = &keys[i].h_cs;
    924           csr.blinded_planchet
    925             = &pd.blinded_planchet.blinded_message->details.cs_blinded_message;
    926           ec = TALER_CRYPTO_helper_cs_batch_sign (
    927             dh,
    928             1,
    929             &csr,
    930             true,
    931             &ds);
    932           if (TALER_EC_NONE != ec)
    933             break;
    934           delay = GNUNET_TIME_absolute_get_duration (start);
    935           duration = GNUNET_TIME_relative_add (duration,
    936                                                delay);
    937           TALER_blinded_denom_sig_free (&ds);
    938           j++;
    939           if (NUM_SIGN_PERFS <= j)
    940             break;
    941         }
    942         TALER_blinded_planchet_free (&pd.blinded_planchet);
    943       }
    944     }   /* for i */
    945   }   /* for j */
    946   fprintf (stderr,
    947            "%u (%s) signature operations took %s\n",
    948            (unsigned int) NUM_SIGN_PERFS,
    949            type,
    950            GNUNET_STRINGS_relative_time_to_string (duration,
    951                                                    GNUNET_YES));
    952   return 0;
    953 }
    954 
    955 
    956 /**
    957  * Parallel signing logic.
    958  *
    959  * @param esh handle to the helper
    960  * @return 0 on success
    961  */
    962 static int
    963 par_signing (struct GNUNET_CONFIGURATION_Handle *cfg)
    964 {
    965   struct GNUNET_TIME_Absolute start;
    966   struct GNUNET_TIME_Relative duration;
    967   pid_t pids[NUM_CORES];
    968   struct TALER_CRYPTO_CsDenominationHelper *dh;
    969 
    970   start = GNUNET_TIME_absolute_get ();
    971   for (unsigned int i = 0; i<NUM_CORES; i++)
    972   {
    973     pids[i] = fork ();
    974     num_keys = 0;
    975     GNUNET_assert (-1 != pids[i]);
    976     if (0 == pids[i])
    977     {
    978       int ret;
    979 
    980       dh = TALER_CRYPTO_helper_cs_connect (cfg,
    981                                            "taler-exchange",
    982                                            &key_cb,
    983                                            NULL);
    984       GNUNET_assert (NULL != dh);
    985       ret = perf_signing (dh,
    986                           "parallel");
    987       TALER_CRYPTO_helper_cs_disconnect (dh);
    988       free_keys ();
    989       exit (ret);
    990     }
    991   }
    992   for (unsigned int i = 0; i<NUM_CORES; i++)
    993   {
    994     int wstatus;
    995 
    996     GNUNET_assert (pids[i] ==
    997                    waitpid (pids[i],
    998                             &wstatus,
    999                             0));
   1000   }
   1001   duration = GNUNET_TIME_absolute_get_duration (start);
   1002   fprintf (stderr,
   1003            "%u (parallel) signature operations took %s (total real time)\n",
   1004            (unsigned int) NUM_SIGN_PERFS * NUM_CORES,
   1005            GNUNET_STRINGS_relative_time_to_string (duration,
   1006                                                    GNUNET_YES));
   1007   return 0;
   1008 }
   1009 
   1010 
   1011 /**
   1012  * Main entry point into the test logic with the helper already running.
   1013  */
   1014 static int
   1015 run_test (void)
   1016 {
   1017   struct GNUNET_CONFIGURATION_Handle *cfg;
   1018   struct TALER_CRYPTO_CsDenominationHelper *dh;
   1019   struct timespec req = {
   1020     .tv_nsec = 250000000
   1021   };
   1022   int ret;
   1023 
   1024   cfg = GNUNET_CONFIGURATION_create (TALER_EXCHANGE_project_data ());
   1025   if (GNUNET_OK !=
   1026       GNUNET_CONFIGURATION_load (cfg,
   1027                                  "test_helper_cs.conf"))
   1028   {
   1029     GNUNET_break (0);
   1030     return 77;
   1031   }
   1032 
   1033   fprintf (stderr, "Waiting for helper to start ... ");
   1034   for (unsigned int i = 0; i<100; i++)
   1035   {
   1036     nanosleep (&req,
   1037                NULL);
   1038     dh = TALER_CRYPTO_helper_cs_connect (cfg,
   1039                                          "taler-exchange",
   1040                                          &key_cb,
   1041                                          NULL);
   1042     if (NULL != dh)
   1043       break;
   1044     fprintf (stderr, ".");
   1045   }
   1046   if (NULL == dh)
   1047   {
   1048     fprintf (stderr,
   1049              "\nFAILED: timeout trying to connect to helper\n");
   1050     GNUNET_CONFIGURATION_destroy (cfg);
   1051     return 1;
   1052   }
   1053   if (0 == num_keys)
   1054   {
   1055     fprintf (stderr,
   1056              "\nFAILED: timeout trying to connect to helper\n");
   1057     TALER_CRYPTO_helper_cs_disconnect (dh);
   1058     GNUNET_CONFIGURATION_destroy (cfg);
   1059     return 1;
   1060   }
   1061   fprintf (stderr,
   1062            " Done (%u keys)\n",
   1063            num_keys);
   1064   ret = 0;
   1065   if (0 == ret)
   1066     ret = test_revocation (dh);
   1067   if (0 == ret)
   1068     ret = test_r_derive (dh);
   1069   if (0 == ret)
   1070     ret = test_signing (dh);
   1071   if (0 == ret)
   1072     ret = test_batch_signing (dh,
   1073                               2,
   1074                               true);
   1075   if (0 == ret)
   1076     ret = test_batch_signing (dh,
   1077                               64,
   1078                               true);
   1079   for (unsigned int i = 0; i<4; i++)
   1080   {
   1081     static unsigned int batches[] = { 1, 4, 16, 64 };
   1082     unsigned int batch_size = batches[i];
   1083     struct GNUNET_TIME_Absolute start;
   1084     struct GNUNET_TIME_Relative duration;
   1085 
   1086     start = GNUNET_TIME_absolute_get ();
   1087     if (0 != ret)
   1088       break;
   1089     ret = test_batch_signing (dh,
   1090                               batch_size,
   1091                               false);
   1092     duration = GNUNET_TIME_absolute_get_duration (start);
   1093     fprintf (stderr,
   1094              "%4u (batch) signature operations took %s (total real time)\n",
   1095              (unsigned int) batch_size,
   1096              GNUNET_STRINGS_relative_time_to_string (duration,
   1097                                                      GNUNET_YES));
   1098   }
   1099   if (0 == ret)
   1100     ret = perf_signing (dh,
   1101                         "sequential");
   1102   TALER_CRYPTO_helper_cs_disconnect (dh);
   1103   free_keys ();
   1104   if (0 == ret)
   1105     ret = par_signing (cfg);
   1106   /* clean up our state */
   1107   GNUNET_CONFIGURATION_destroy (cfg);
   1108   return ret;
   1109 }
   1110 
   1111 
   1112 int
   1113 main (int argc,
   1114       const char *const argv[])
   1115 {
   1116   struct GNUNET_Process *helper;
   1117   char *libexec_dir;
   1118   char *binary_name;
   1119   int ret;
   1120   enum GNUNET_OS_ProcessStatusType type;
   1121   unsigned long code;
   1122   const char *loglev = "WARNING";
   1123 
   1124   (void) argc;
   1125   (void) argv;
   1126   unsetenv ("XDG_DATA_HOME");
   1127   unsetenv ("XDG_CONFIG_HOME");
   1128   GNUNET_log_setup ("test-helper-cs",
   1129                     loglev,
   1130                     NULL);
   1131   libexec_dir = GNUNET_OS_installation_get_path (TALER_EXCHANGE_project_data (),
   1132                                                  GNUNET_OS_IPK_BINDIR);
   1133   GNUNET_asprintf (&binary_name,
   1134                    "%s/%s",
   1135                    libexec_dir,
   1136                    "taler-exchange-secmod-cs");
   1137   GNUNET_free (libexec_dir);
   1138   helper = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR);
   1139   if (GNUNET_OK !=
   1140       GNUNET_process_run_command_va (helper,
   1141                                      binary_name,
   1142                                      binary_name,
   1143                                      "-c",
   1144                                      "test_helper_cs.conf",
   1145                                      "-L",
   1146                                      loglev,
   1147                                      NULL))
   1148   {
   1149     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
   1150                               "exec",
   1151                               binary_name);
   1152     GNUNET_process_destroy (helper);
   1153     GNUNET_free (binary_name);
   1154     return 77;
   1155   }
   1156   GNUNET_free (binary_name);
   1157   ret = run_test ();
   1158 
   1159   GNUNET_break (GNUNET_OK ==
   1160                 GNUNET_process_kill (helper,
   1161                                      SIGTERM));
   1162   if (GNUNET_OK !=
   1163       GNUNET_process_wait (helper,
   1164                            true,
   1165                            &type,
   1166                            &code))
   1167   {
   1168     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1169                 "Helper process did not die voluntarily, killing hard\n");
   1170     GNUNET_break (GNUNET_OK ==
   1171                   GNUNET_process_kill (helper,
   1172                                        SIGKILL));
   1173     ret = 4;
   1174   }
   1175   else if ( (GNUNET_OS_PROCESS_EXITED != type) ||
   1176             (0 != code) )
   1177   {
   1178     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1179                 "Helper died with unexpected status %d/%d\n",
   1180                 (int) type,
   1181                 (int) code);
   1182     ret = 5;
   1183   }
   1184   GNUNET_process_destroy (helper);
   1185   return ret;
   1186 }
   1187 
   1188 
   1189 /* end of test_helper_cs.c */