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


      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 "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 (num_keys);
    187     /* find index of key to revoke */
    188     for (unsigned int j = 0; j < MAX_KEYS; j++)
    189     {
    190       if (! keys[j].valid)
    191         continue;
    192       if (0 != off)
    193       {
    194         off--;
    195         continue;
    196       }
    197       keys[j].revoked = true;
    198       fprintf (stderr,
    199                "Revoking key %s (%u) ...",
    200                TALER_B2S (&keys[j].exchange_pub),
    201                j);
    202       TALER_CRYPTO_helper_esign_revoke (esh,
    203                                         &keys[j].exchange_pub);
    204       for (unsigned int k = 0; k<1000; k++)
    205       {
    206         TALER_CRYPTO_helper_esign_poll (esh);
    207         if ( (! keys[j].revoked) ||
    208              (GNUNET_TIME_absolute_is_past (
    209                 GNUNET_TIME_absolute_add (keys[j].start_time.abs_time,
    210                                           keys[j].validity_duration))) )
    211         {
    212           break;
    213         }
    214         nanosleep (&req, NULL);
    215         fprintf (stderr, ".");
    216       }
    217       if ( (keys[j].revoked) &&
    218            (! GNUNET_TIME_absolute_is_past (
    219               GNUNET_TIME_absolute_add (keys[j].start_time.abs_time,
    220                                         keys[j].validity_duration))) )
    221       {
    222         fprintf (stderr,
    223                  "\nFAILED: timeout trying to revoke key %u\n",
    224                  j);
    225         TALER_CRYPTO_helper_esign_disconnect (esh);
    226         esh = NULL;
    227         return 2;
    228       }
    229       fprintf (stderr, "\n");
    230       break;
    231     }
    232   }
    233   return 0;
    234 }
    235 
    236 
    237 /**
    238  * Test signing logic.
    239  *
    240  * @param esh handle to the helper
    241  * @return 0 on success
    242  */
    243 static int
    244 test_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh)
    245 {
    246   struct GNUNET_CRYPTO_SignaturePurpose purpose = {
    247     .purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST),
    248     .size = htonl (sizeof (purpose)),
    249   };
    250 
    251   for (unsigned int i = 0; i<NUM_SIGN_TESTS; i++)
    252   {
    253     struct TALER_ExchangePublicKeyP exchange_pub;
    254     struct TALER_ExchangeSignatureP exchange_sig;
    255     enum TALER_ErrorCode ec;
    256 
    257     ec = TALER_CRYPTO_helper_esign_sign_ (esh,
    258                                           &purpose,
    259                                           &exchange_pub,
    260                                           &exchange_sig);
    261     switch (ec)
    262     {
    263     case TALER_EC_NONE:
    264       if (GNUNET_OK !=
    265           GNUNET_CRYPTO_eddsa_verify_ (GNUNET_SIGNATURE_PURPOSE_TEST,
    266                                        &purpose,
    267                                        &exchange_sig.eddsa_signature,
    268                                        &exchange_pub.eddsa_pub))
    269       {
    270         /* signature invalid */
    271         GNUNET_break (0);
    272         return 17;
    273       }
    274       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    275                   "Received valid signature\n");
    276       break;
    277     default:
    278       /* unexpected error */
    279       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    280                   "Unexpected error %d\n",
    281                   ec);
    282       return 7;
    283     }
    284   }
    285   return 0;
    286 }
    287 
    288 
    289 /**
    290  * Benchmark signing logic.
    291  *
    292  * @param esh handle to the helper
    293  * @return 0 on success
    294  */
    295 static int
    296 perf_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh,
    297               const char *type)
    298 {
    299   struct GNUNET_CRYPTO_SignaturePurpose purpose = {
    300     .purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST),
    301     .size = htonl (sizeof (purpose)),
    302   };
    303   struct GNUNET_TIME_Relative duration;
    304 
    305   duration = GNUNET_TIME_UNIT_ZERO;
    306   for (unsigned int j = 0; j<NUM_SIGN_PERFS; j++)
    307   {
    308     struct GNUNET_TIME_Relative delay;
    309     struct TALER_ExchangePublicKeyP exchange_pub;
    310     struct TALER_ExchangeSignatureP exchange_sig;
    311     enum TALER_ErrorCode ec;
    312     struct GNUNET_TIME_Absolute start;
    313 
    314     TALER_CRYPTO_helper_esign_poll (esh);
    315     start = GNUNET_TIME_absolute_get ();
    316     ec = TALER_CRYPTO_helper_esign_sign_ (esh,
    317                                           &purpose,
    318                                           &exchange_pub,
    319                                           &exchange_sig);
    320     if (TALER_EC_NONE != ec)
    321     {
    322       GNUNET_break (0);
    323       return 42;
    324     }
    325     delay = GNUNET_TIME_absolute_get_duration (start);
    326     duration = GNUNET_TIME_relative_add (duration,
    327                                          delay);
    328   } /* for j */
    329   fprintf (stderr,
    330            "%u (%s) signature operations took %s\n",
    331            (unsigned int) NUM_SIGN_PERFS,
    332            type,
    333            GNUNET_STRINGS_relative_time_to_string (duration,
    334                                                    GNUNET_YES));
    335   return 0;
    336 }
    337 
    338 
    339 /**
    340  * Parallel signing logic.
    341  *
    342  * @param esh handle to the helper
    343  * @return 0 on success
    344  */
    345 static int
    346 par_signing (struct GNUNET_CONFIGURATION_Handle *cfg)
    347 {
    348   struct GNUNET_TIME_Absolute start;
    349   struct GNUNET_TIME_Relative duration;
    350   pid_t pids[NUM_CORES];
    351 
    352   memset (keys,
    353           0,
    354           sizeof (keys));
    355   num_keys = 0;
    356   start = GNUNET_TIME_absolute_get ();
    357   for (unsigned int i = 0; i<NUM_CORES; i++)
    358   {
    359     pids[i] = fork ();
    360     GNUNET_assert (-1 != pids[i]);
    361     if (0 == pids[i])
    362     {
    363       struct TALER_CRYPTO_ExchangeSignHelper *esh;
    364       int ret;
    365 
    366       esh = TALER_CRYPTO_helper_esign_connect (cfg,
    367                                                "taler-exchange",
    368                                                &key_cb,
    369                                                NULL);
    370       if (NULL == esh)
    371       {
    372         GNUNET_break (0);
    373         exit (EXIT_FAILURE);
    374       }
    375       ret = perf_signing (esh,
    376                           "parallel");
    377       TALER_CRYPTO_helper_esign_disconnect (esh);
    378       exit (ret);
    379     }
    380   }
    381   for (unsigned int i = 0; i<NUM_CORES; i++)
    382   {
    383     int wstatus;
    384 
    385     GNUNET_assert (pids[i] ==
    386                    waitpid (pids[i],
    387                             &wstatus,
    388                             0));
    389   }
    390   duration = GNUNET_TIME_absolute_get_duration (start);
    391   fprintf (stderr,
    392            "%u (parallel) signature operations took %s (total real time)\n",
    393            (unsigned int) NUM_SIGN_PERFS * NUM_CORES,
    394            GNUNET_STRINGS_relative_time_to_string (duration,
    395                                                    true));
    396   return 0;
    397 }
    398 
    399 
    400 /**
    401  * Main entry point into the test logic with the helper already running.
    402  */
    403 static int
    404 run_test (void)
    405 {
    406   struct GNUNET_CONFIGURATION_Handle *cfg;
    407   struct TALER_CRYPTO_ExchangeSignHelper *esh;
    408   int ret;
    409   struct timespec req = {
    410     .tv_nsec = 250000000
    411   };
    412 
    413   cfg = GNUNET_CONFIGURATION_create (TALER_EXCHANGE_project_data ());
    414   if (GNUNET_OK !=
    415       GNUNET_CONFIGURATION_load (cfg,
    416                                  "test_helper_eddsa.conf"))
    417   {
    418     GNUNET_break (0);
    419     return 77;
    420   }
    421 
    422   /* wait for helper to start and give us keys */
    423   fprintf (stderr, "Waiting for helper to start ... ");
    424   for (unsigned int i = 0; i<100; i++)
    425   {
    426     nanosleep (&req,
    427                NULL);
    428     esh = TALER_CRYPTO_helper_esign_connect (cfg,
    429                                              "taler-exchange",
    430                                              &key_cb,
    431                                              NULL);
    432     if (NULL != esh)
    433       break;
    434     fprintf (stderr, ".");
    435   }
    436   if (NULL == esh)
    437   {
    438     fprintf (stderr,
    439              "\nFAILED: timeout trying to connect to helper\n");
    440     GNUNET_CONFIGURATION_destroy (cfg);
    441     return 1;
    442   }
    443   if (0 == num_keys)
    444   {
    445     fprintf (stderr,
    446              "\nFAILED: no keys returned by helper\n");
    447     TALER_CRYPTO_helper_esign_disconnect (esh);
    448     esh = NULL;
    449     GNUNET_CONFIGURATION_destroy (cfg);
    450     return 1;
    451   }
    452   fprintf (stderr,
    453            " Done (%u keys)\n",
    454            num_keys);
    455   ret = 0;
    456   if (0 == ret)
    457     ret = test_revocation (esh);
    458   if (0 == ret)
    459     ret = test_signing (esh);
    460   if (0 == ret)
    461     ret = perf_signing (esh,
    462                         "sequential");
    463   if (NULL != esh)
    464   {
    465     TALER_CRYPTO_helper_esign_disconnect (esh);
    466     esh = NULL;
    467   }
    468   if (0 == ret)
    469     ret = par_signing (cfg);
    470   /* clean up our state */
    471   for (unsigned int i = 0; i<MAX_KEYS; i++)
    472     if (keys[i].valid)
    473     {
    474       keys[i].valid = false;
    475       GNUNET_assert (num_keys > 0);
    476       num_keys--;
    477     }
    478   GNUNET_CONFIGURATION_destroy (cfg);
    479   return ret;
    480 }
    481 
    482 
    483 int
    484 main (int argc,
    485       const char *const argv[])
    486 {
    487   struct GNUNET_Process *helper;
    488   char *libexec_dir;
    489   char *binary_name;
    490   int ret;
    491   enum GNUNET_OS_ProcessStatusType type;
    492   unsigned long code;
    493 
    494   (void) argc;
    495   (void) argv;
    496   unsetenv ("XDG_DATA_HOME");
    497   unsetenv ("XDG_CONFIG_HOME");
    498   GNUNET_log_setup ("test-helper-eddsa",
    499                     "INFO",
    500                     NULL);
    501   libexec_dir = GNUNET_OS_installation_get_path (TALER_EXCHANGE_project_data (),
    502                                                  GNUNET_OS_IPK_BINDIR);
    503   GNUNET_asprintf (&binary_name,
    504                    "%s/%s",
    505                    libexec_dir,
    506                    "taler-exchange-secmod-eddsa");
    507   GNUNET_free (libexec_dir);
    508   helper = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR);
    509   if (GNUNET_OK !=
    510       GNUNET_process_run_command_va (helper,
    511                                      binary_name,
    512                                      binary_name,
    513                                      "-c",
    514                                      "test_helper_eddsa.conf",
    515                                      "-L",
    516                                      "INFO",
    517                                      NULL))
    518   {
    519     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    520                               "exec",
    521                               binary_name);
    522     GNUNET_process_destroy (helper);
    523     GNUNET_free (binary_name);
    524     return 77;
    525   }
    526   GNUNET_free (binary_name);
    527   ret = run_test ();
    528 
    529   GNUNET_break (GNUNET_OK ==
    530                 GNUNET_process_kill (helper,
    531                                      SIGTERM));
    532   if (GNUNET_OK !=
    533       GNUNET_process_wait (helper,
    534                            true,
    535                            &type,
    536                            &code))
    537   {
    538     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    539                 "Helper process did not die voluntarily, killing hard\n");
    540     GNUNET_break (GNUNET_OK ==
    541                   GNUNET_process_kill (helper,
    542                                        SIGKILL));
    543     ret = 4;
    544   }
    545   else if ( (GNUNET_OS_PROCESS_EXITED != type) ||
    546             (0 != code) )
    547   {
    548     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    549                 "Helper died with unexpected status %d/%d\n",
    550                 (int) type,
    551                 (int) code);
    552     ret = 5;
    553   }
    554   GNUNET_process_destroy (helper);
    555   return ret;
    556 }
    557 
    558 
    559 /* end of test_helper_eddsa.c */