exchange

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

test_helper_eddsa.c (15337B)


      1 /*
      2   This file is part of TALER
      3   (C) 2020, 2021 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_eddsa.c
     18  * @brief Tests for EDDSA crypto helper
     19  * @author Christian Grothoff
     20  */
     21 #include "taler/platform.h"
     22 #include "taler/taler_util.h"
     23 #include <gnunet/gnunet_signatures.h>
     24 
     25 /**
     26  * Configuration has 1 minute duration and 5 minutes lookahead, so
     27  * we should never have more than 6 active keys, plus for during
     28  * key expiration / revocation.
     29  */
     30 #define MAX_KEYS 20
     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  * during the test phase?
     40  */
     41 #define NUM_SIGN_TESTS 3
     42 
     43 /**
     44  * How many iterations of the successful signing test should we run
     45  * during the benchmark phase?
     46  */
     47 #define NUM_SIGN_PERFS 100
     48 
     49 /**
     50  * How many parallel clients should we use for the parallel
     51  * benchmark? (> 500 may cause problems with the max open FD number limit).
     52  */
     53 #define NUM_CORES 8
     54 
     55 /**
     56  * Number of keys currently in #keys.
     57  */
     58 static unsigned int num_keys;
     59 
     60 /**
     61  * Keys currently managed by the helper.
     62  */
     63 struct KeyData
     64 {
     65   /**
     66    * Validity start point.
     67    */
     68   struct GNUNET_TIME_Timestamp start_time;
     69 
     70   /**
     71    * Key expires for signing at @e start_time plus this value.
     72    */
     73   struct GNUNET_TIME_Relative validity_duration;
     74 
     75   /**
     76    * Full public key.
     77    */
     78   struct TALER_ExchangePublicKeyP exchange_pub;
     79 
     80   /**
     81    * Is this key currently valid?
     82    */
     83   bool valid;
     84 
     85   /**
     86    * Did the test driver revoke this key?
     87    */
     88   bool revoked;
     89 };
     90 
     91 /**
     92  * Array of all the keys we got from the helper.
     93  */
     94 static struct KeyData keys[MAX_KEYS];
     95 
     96 
     97 /**
     98  * Function called with information about available keys for signing.  Usually
     99  * only called once per key upon connect. Also called again in case a key is
    100  * being revoked, in that case with an @a end_time of zero.  Stores the keys
    101  * status in #keys.
    102  *
    103  * @param cls closure, NULL
    104  * @param start_time when does the key become available for signing;
    105  *                 zero if the key has been revoked or purged
    106  * @param validity_duration how long does the key remain available for signing;
    107  *                 zero if the key has been revoked or purged
    108  * @param exchange_pub the public key itself
    109  * @param sm_pub public key of the security module, NULL if the key was revoked or purged
    110  * @param sm_sig signature from the security module, NULL if the key was revoked or purged
    111  *               The signature was already verified against @a sm_pub.
    112  */
    113 static void
    114 key_cb (void *cls,
    115         struct GNUNET_TIME_Timestamp start_time,
    116         struct GNUNET_TIME_Relative validity_duration,
    117         const struct TALER_ExchangePublicKeyP *exchange_pub,
    118         const struct TALER_SecurityModulePublicKeyP *sm_pub,
    119         const struct TALER_SecurityModuleSignatureP *sm_sig)
    120 {
    121   (void) cls;
    122   (void) sm_pub;
    123   (void) sm_sig;
    124 
    125   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    126               "Update on key %s (%s)...",
    127               TALER_B2S (exchange_pub),
    128               GNUNET_STRINGS_relative_time_to_string (validity_duration,
    129                                                       GNUNET_YES));
    130 
    131   if (GNUNET_TIME_relative_is_zero (validity_duration))
    132   {
    133     bool found = false;
    134 
    135     for (unsigned int i = 0; i<MAX_KEYS; i++)
    136       if (0 == GNUNET_memcmp (exchange_pub,
    137                               &keys[i].exchange_pub))
    138       {
    139         keys[i].valid = false;
    140         keys[i].revoked = false;
    141         GNUNET_assert (num_keys > 0);
    142         num_keys--;
    143         found = true;
    144         break;
    145       }
    146     if (! found)
    147       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    148                   "Error: helper announced expiration of unknown key!\n");
    149 
    150     return;
    151   }
    152   for (unsigned int i = 0; i<MAX_KEYS; i++)
    153     if (! keys[i].valid)
    154     {
    155       keys[i].valid = true;
    156       keys[i].exchange_pub = *exchange_pub;
    157       keys[i].start_time = start_time;
    158       keys[i].validity_duration = validity_duration;
    159       num_keys++;
    160       return;
    161     }
    162   /* too many keys! */
    163   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    164               "Error: received %d live keys from the service!\n",
    165               MAX_KEYS + 1);
    166 }
    167 
    168 
    169 /**
    170  * Test key revocation logic.
    171  *
    172  * @param esh handle to the helper
    173  * @return 0 on success
    174  */
    175 static int
    176 test_revocation (struct TALER_CRYPTO_ExchangeSignHelper *esh)
    177 {
    178   struct timespec req = {
    179     .tv_nsec = 250000000
    180   };
    181 
    182   for (unsigned int i = 0; i<NUM_REVOKES; i++)
    183   {
    184     uint32_t off;
    185 
    186     off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
    187                                     num_keys);
    188     /* find index of key to revoke */
    189     for (unsigned int j = 0; j < MAX_KEYS; j++)
    190     {
    191       if (! keys[j].valid)
    192         continue;
    193       if (0 != off)
    194       {
    195         off--;
    196         continue;
    197       }
    198       keys[j].revoked = true;
    199       fprintf (stderr,
    200                "Revoking key %s (%u) ...",
    201                TALER_B2S (&keys[j].exchange_pub),
    202                j);
    203       TALER_CRYPTO_helper_esign_revoke (esh,
    204                                         &keys[j].exchange_pub);
    205       for (unsigned int k = 0; k<1000; k++)
    206       {
    207         TALER_CRYPTO_helper_esign_poll (esh);
    208         if ( (! keys[j].revoked) ||
    209              (GNUNET_TIME_absolute_is_past (
    210                 GNUNET_TIME_absolute_add (keys[j].start_time.abs_time,
    211                                           keys[j].validity_duration))) )
    212         {
    213           break;
    214         }
    215         nanosleep (&req, NULL);
    216         fprintf (stderr, ".");
    217       }
    218       if ( (keys[j].revoked) &&
    219            (! GNUNET_TIME_absolute_is_past (
    220               GNUNET_TIME_absolute_add (keys[j].start_time.abs_time,
    221                                         keys[j].validity_duration))) )
    222       {
    223         fprintf (stderr,
    224                  "\nFAILED: timeout trying to revoke key %u\n",
    225                  j);
    226         TALER_CRYPTO_helper_esign_disconnect (esh);
    227         esh = NULL;
    228         return 2;
    229       }
    230       fprintf (stderr, "\n");
    231       break;
    232     }
    233   }
    234   return 0;
    235 }
    236 
    237 
    238 /**
    239  * Test signing logic.
    240  *
    241  * @param esh handle to the helper
    242  * @return 0 on success
    243  */
    244 static int
    245 test_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh)
    246 {
    247   struct GNUNET_CRYPTO_SignaturePurpose purpose = {
    248     .purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST),
    249     .size = htonl (sizeof (purpose)),
    250   };
    251 
    252   for (unsigned int i = 0; i<NUM_SIGN_TESTS; i++)
    253   {
    254     struct TALER_ExchangePublicKeyP exchange_pub;
    255     struct TALER_ExchangeSignatureP exchange_sig;
    256     enum TALER_ErrorCode ec;
    257 
    258     ec = TALER_CRYPTO_helper_esign_sign_ (esh,
    259                                           &purpose,
    260                                           &exchange_pub,
    261                                           &exchange_sig);
    262     switch (ec)
    263     {
    264     case TALER_EC_NONE:
    265       if (GNUNET_OK !=
    266           GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
    267                                        &purpose,
    268                                        &exchange_sig.eddsa_signature,
    269                                        &exchange_pub.eddsa_pub))
    270       {
    271         /* signature invalid */
    272         GNUNET_break (0);
    273         return 17;
    274       }
    275       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    276                   "Received valid signature\n");
    277       break;
    278     default:
    279       /* unexpected error */
    280       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    281                   "Unexpected error %d\n",
    282                   ec);
    283       return 7;
    284     }
    285   }
    286   return 0;
    287 }
    288 
    289 
    290 /**
    291  * Benchmark signing logic.
    292  *
    293  * @param esh handle to the helper
    294  * @return 0 on success
    295  */
    296 static int
    297 perf_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh,
    298               const char *type)
    299 {
    300   struct GNUNET_CRYPTO_SignaturePurpose purpose = {
    301     .purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST),
    302     .size = htonl (sizeof (purpose)),
    303   };
    304   struct GNUNET_TIME_Relative duration;
    305 
    306   duration = GNUNET_TIME_UNIT_ZERO;
    307   for (unsigned int j = 0; j<NUM_SIGN_PERFS; j++)
    308   {
    309     struct GNUNET_TIME_Relative delay;
    310     struct TALER_ExchangePublicKeyP exchange_pub;
    311     struct TALER_ExchangeSignatureP exchange_sig;
    312     enum TALER_ErrorCode ec;
    313     struct GNUNET_TIME_Absolute start;
    314 
    315     TALER_CRYPTO_helper_esign_poll (esh);
    316     start = GNUNET_TIME_absolute_get ();
    317     ec = TALER_CRYPTO_helper_esign_sign_ (esh,
    318                                           &purpose,
    319                                           &exchange_pub,
    320                                           &exchange_sig);
    321     if (TALER_EC_NONE != ec)
    322     {
    323       GNUNET_break (0);
    324       return 42;
    325     }
    326     delay = GNUNET_TIME_absolute_get_duration (start);
    327     duration = GNUNET_TIME_relative_add (duration,
    328                                          delay);
    329   } /* for j */
    330   fprintf (stderr,
    331            "%u (%s) signature operations took %s\n",
    332            (unsigned int) NUM_SIGN_PERFS,
    333            type,
    334            GNUNET_STRINGS_relative_time_to_string (duration,
    335                                                    GNUNET_YES));
    336   return 0;
    337 }
    338 
    339 
    340 /**
    341  * Parallel signing logic.
    342  *
    343  * @param esh handle to the helper
    344  * @return 0 on success
    345  */
    346 static int
    347 par_signing (struct GNUNET_CONFIGURATION_Handle *cfg)
    348 {
    349   struct GNUNET_TIME_Absolute start;
    350   struct GNUNET_TIME_Relative duration;
    351   pid_t pids[NUM_CORES];
    352 
    353   memset (keys,
    354           0,
    355           sizeof (keys));
    356   num_keys = 0;
    357   start = GNUNET_TIME_absolute_get ();
    358   for (unsigned int i = 0; i<NUM_CORES; i++)
    359   {
    360     pids[i] = fork ();
    361     GNUNET_assert (-1 != pids[i]);
    362     if (0 == pids[i])
    363     {
    364       struct TALER_CRYPTO_ExchangeSignHelper *esh;
    365       int ret;
    366 
    367       esh = TALER_CRYPTO_helper_esign_connect (cfg,
    368                                                "taler-exchange",
    369                                                &key_cb,
    370                                                NULL);
    371       if (NULL == esh)
    372       {
    373         GNUNET_break (0);
    374         exit (EXIT_FAILURE);
    375       }
    376       ret = perf_signing (esh,
    377                           "parallel");
    378       TALER_CRYPTO_helper_esign_disconnect (esh);
    379       exit (ret);
    380     }
    381   }
    382   for (unsigned int i = 0; i<NUM_CORES; i++)
    383   {
    384     int wstatus;
    385 
    386     GNUNET_assert (pids[i] ==
    387                    waitpid (pids[i],
    388                             &wstatus,
    389                             0));
    390   }
    391   duration = GNUNET_TIME_absolute_get_duration (start);
    392   fprintf (stderr,
    393            "%u (parallel) signature operations took %s (total real time)\n",
    394            (unsigned int) NUM_SIGN_PERFS * NUM_CORES,
    395            GNUNET_STRINGS_relative_time_to_string (duration,
    396                                                    true));
    397   return 0;
    398 }
    399 
    400 
    401 /**
    402  * Main entry point into the test logic with the helper already running.
    403  */
    404 static int
    405 run_test (void)
    406 {
    407   struct GNUNET_CONFIGURATION_Handle *cfg;
    408   struct TALER_CRYPTO_ExchangeSignHelper *esh;
    409   int ret;
    410   struct timespec req = {
    411     .tv_nsec = 250000000
    412   };
    413 
    414   cfg = GNUNET_CONFIGURATION_create (TALER_EXCHANGE_project_data ());
    415   if (GNUNET_OK !=
    416       GNUNET_CONFIGURATION_load (cfg,
    417                                  "test_helper_eddsa.conf"))
    418   {
    419     GNUNET_break (0);
    420     return 77;
    421   }
    422 
    423   /* wait for helper to start and give us keys */
    424   fprintf (stderr, "Waiting for helper to start ... ");
    425   for (unsigned int i = 0; i<100; i++)
    426   {
    427     nanosleep (&req,
    428                NULL);
    429     esh = TALER_CRYPTO_helper_esign_connect (cfg,
    430                                              "taler-exchange",
    431                                              &key_cb,
    432                                              NULL);
    433     if (NULL != esh)
    434       break;
    435     fprintf (stderr, ".");
    436   }
    437   if (NULL == esh)
    438   {
    439     fprintf (stderr,
    440              "\nFAILED: timeout trying to connect to helper\n");
    441     GNUNET_CONFIGURATION_destroy (cfg);
    442     return 1;
    443   }
    444   if (0 == num_keys)
    445   {
    446     fprintf (stderr,
    447              "\nFAILED: no keys returned by helper\n");
    448     TALER_CRYPTO_helper_esign_disconnect (esh);
    449     esh = NULL;
    450     GNUNET_CONFIGURATION_destroy (cfg);
    451     return 1;
    452   }
    453   fprintf (stderr,
    454            " Done (%u keys)\n",
    455            num_keys);
    456   ret = 0;
    457   if (0 == ret)
    458     ret = test_revocation (esh);
    459   if (0 == ret)
    460     ret = test_signing (esh);
    461   if (0 == ret)
    462     ret = perf_signing (esh,
    463                         "sequential");
    464   if (NULL != esh)
    465   {
    466     TALER_CRYPTO_helper_esign_disconnect (esh);
    467     esh = NULL;
    468   }
    469   if (0 == ret)
    470     ret = par_signing (cfg);
    471   /* clean up our state */
    472   for (unsigned int i = 0; i<MAX_KEYS; i++)
    473     if (keys[i].valid)
    474     {
    475       keys[i].valid = false;
    476       GNUNET_assert (num_keys > 0);
    477       num_keys--;
    478     }
    479   GNUNET_CONFIGURATION_destroy (cfg);
    480   return ret;
    481 }
    482 
    483 
    484 int
    485 main (int argc,
    486       const char *const argv[])
    487 {
    488   struct GNUNET_OS_Process *helper;
    489   char *libexec_dir;
    490   char *binary_name;
    491   int ret;
    492   enum GNUNET_OS_ProcessStatusType type;
    493   unsigned long code;
    494 
    495   (void) argc;
    496   (void) argv;
    497   unsetenv ("XDG_DATA_HOME");
    498   unsetenv ("XDG_CONFIG_HOME");
    499   GNUNET_log_setup ("test-helper-eddsa",
    500                     "INFO",
    501                     NULL);
    502   libexec_dir = GNUNET_OS_installation_get_path (TALER_EXCHANGE_project_data (),
    503                                                  GNUNET_OS_IPK_BINDIR);
    504   GNUNET_asprintf (&binary_name,
    505                    "%s/%s",
    506                    libexec_dir,
    507                    "taler-exchange-secmod-eddsa");
    508   GNUNET_free (libexec_dir);
    509   helper = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
    510                                     NULL, NULL, NULL,
    511                                     binary_name,
    512                                     binary_name,
    513                                     "-c",
    514                                     "test_helper_eddsa.conf",
    515                                     "-L",
    516                                     "INFO",
    517                                     NULL);
    518   if (NULL == helper)
    519   {
    520     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    521                               "exec",
    522                               binary_name);
    523     GNUNET_free (binary_name);
    524     return 77;
    525   }
    526   GNUNET_free (binary_name);
    527   ret = run_test ();
    528 
    529   GNUNET_OS_process_kill (helper,
    530                           SIGTERM);
    531   if (GNUNET_OK !=
    532       GNUNET_OS_process_wait_status (helper,
    533                                      &type,
    534                                      &code))
    535   {
    536     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    537                 "Helper process did not die voluntarily, killing hard\n");
    538     GNUNET_OS_process_kill (helper,
    539                             SIGKILL);
    540     ret = 4;
    541   }
    542   else if ( (GNUNET_OS_PROCESS_EXITED != type) ||
    543             (0 != code) )
    544   {
    545     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    546                 "Helper died with unexpected status %d/%d\n",
    547                 (int) type,
    548                 (int) code);
    549     ret = 5;
    550   }
    551   GNUNET_OS_process_destroy (helper);
    552   return ret;
    553 }
    554 
    555 
    556 /* end of test_helper_eddsa.c */