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


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