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


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