LCOV - code coverage report
Current view: top level - util - test_helper_eddsa.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 78.1 % 178 139
Test Date: 2026-01-04 22:17:00 Functions: 100.0 % 7 7

            Line data    Source code
       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           84 : 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           84 :   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           84 :   if (GNUNET_TIME_relative_is_zero (validity_duration))
     132              :   {
     133            3 :     bool found = false;
     134              : 
     135           14 :     for (unsigned int i = 0; i<MAX_KEYS; i++)
     136           14 :       if (0 == GNUNET_memcmp (exchange_pub,
     137              :                               &keys[i].exchange_pub))
     138              :       {
     139            3 :         keys[i].valid = false;
     140            3 :         keys[i].revoked = false;
     141            3 :         GNUNET_assert (num_keys > 0);
     142            3 :         num_keys--;
     143            3 :         found = true;
     144            3 :         break;
     145              :       }
     146            3 :     if (! found)
     147            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     148              :                   "Error: helper announced expiration of unknown key!\n");
     149              : 
     150            3 :     return;
     151              :   }
     152          395 :   for (unsigned int i = 0; i<MAX_KEYS; i++)
     153          395 :     if (! keys[i].valid)
     154              :     {
     155           81 :       keys[i].valid = true;
     156           81 :       keys[i].exchange_pub = *exchange_pub;
     157           81 :       keys[i].start_time = start_time;
     158           81 :       keys[i].validity_duration = validity_duration;
     159           81 :       num_keys++;
     160           81 :       return;
     161              :     }
     162              :   /* too many keys! */
     163            0 :   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            1 : test_revocation (struct TALER_CRYPTO_ExchangeSignHelper *esh)
     177              : {
     178            1 :   struct timespec req = {
     179              :     .tv_nsec = 250000000
     180              :   };
     181              : 
     182            4 :   for (unsigned int i = 0; i<NUM_REVOKES; i++)
     183              :   {
     184              :     uint32_t off;
     185              : 
     186            3 :     off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
     187              :                                     num_keys);
     188              :     /* find index of key to revoke */
     189           14 :     for (unsigned int j = 0; j < MAX_KEYS; j++)
     190              :     {
     191           14 :       if (! keys[j].valid)
     192            0 :         continue;
     193           14 :       if (0 != off)
     194              :       {
     195           11 :         off--;
     196           11 :         continue;
     197              :       }
     198            3 :       keys[j].revoked = true;
     199            3 :       fprintf (stderr,
     200              :                "Revoking key %s (%u) ...",
     201            3 :                TALER_B2S (&keys[j].exchange_pub),
     202              :                j);
     203            3 :       TALER_CRYPTO_helper_esign_revoke (esh,
     204            3 :                                         &keys[j].exchange_pub);
     205            6 :       for (unsigned int k = 0; k<1000; k++)
     206              :       {
     207            6 :         TALER_CRYPTO_helper_esign_poll (esh);
     208            9 :         if ( (! keys[j].revoked) ||
     209            3 :              (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            3 :         nanosleep (&req, NULL);
     216            3 :         fprintf (stderr, ".");
     217              :       }
     218            3 :       if ( (keys[j].revoked) &&
     219            0 :            (! GNUNET_TIME_absolute_is_past (
     220              :               GNUNET_TIME_absolute_add (keys[j].start_time.abs_time,
     221              :                                         keys[j].validity_duration))) )
     222              :       {
     223            0 :         fprintf (stderr,
     224              :                  "\nFAILED: timeout trying to revoke key %u\n",
     225              :                  j);
     226            0 :         TALER_CRYPTO_helper_esign_disconnect (esh);
     227            0 :         esh = NULL;
     228            0 :         return 2;
     229              :       }
     230            3 :       fprintf (stderr, "\n");
     231            3 :       break;
     232              :     }
     233              :   }
     234            1 :   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            1 : test_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh)
     246              : {
     247            1 :   struct GNUNET_CRYPTO_SignaturePurpose purpose = {
     248            1 :     .purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST),
     249            1 :     .size = htonl (sizeof (purpose)),
     250              :   };
     251              : 
     252            4 :   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            3 :     ec = TALER_CRYPTO_helper_esign_sign_ (esh,
     259              :                                           &purpose,
     260              :                                           &exchange_pub,
     261              :                                           &exchange_sig);
     262            3 :     switch (ec)
     263              :     {
     264            3 :     case TALER_EC_NONE:
     265            3 :       if (GNUNET_OK !=
     266            3 :           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            0 :         GNUNET_break (0);
     273            0 :         return 17;
     274              :       }
     275            3 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     276              :                   "Received valid signature\n");
     277            3 :       break;
     278            0 :     default:
     279              :       /* unexpected error */
     280            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     281              :                   "Unexpected error %d\n",
     282              :                   ec);
     283            0 :       return 7;
     284              :     }
     285              :   }
     286            1 :   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            9 : perf_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh,
     298              :               const char *type)
     299              : {
     300            9 :   struct GNUNET_CRYPTO_SignaturePurpose purpose = {
     301            9 :     .purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST),
     302            9 :     .size = htonl (sizeof (purpose)),
     303              :   };
     304              :   struct GNUNET_TIME_Relative duration;
     305              : 
     306            9 :   duration = GNUNET_TIME_UNIT_ZERO;
     307          909 :   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          900 :     TALER_CRYPTO_helper_esign_poll (esh);
     316          900 :     start = GNUNET_TIME_absolute_get ();
     317          900 :     ec = TALER_CRYPTO_helper_esign_sign_ (esh,
     318              :                                           &purpose,
     319              :                                           &exchange_pub,
     320              :                                           &exchange_sig);
     321          900 :     if (TALER_EC_NONE != ec)
     322              :     {
     323            0 :       GNUNET_break (0);
     324            0 :       return 42;
     325              :     }
     326          900 :     delay = GNUNET_TIME_absolute_get_duration (start);
     327          900 :     duration = GNUNET_TIME_relative_add (duration,
     328              :                                          delay);
     329              :   } /* for j */
     330            9 :   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            9 :   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            1 : 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            1 :   memset (keys,
     354              :           0,
     355              :           sizeof (keys));
     356            1 :   num_keys = 0;
     357            1 :   start = GNUNET_TIME_absolute_get ();
     358            9 :   for (unsigned int i = 0; i<NUM_CORES; i++)
     359              :   {
     360            8 :     pids[i] = fork ();
     361           16 :     GNUNET_assert (-1 != pids[i]);
     362           16 :     if (0 == pids[i])
     363              :     {
     364              :       struct TALER_CRYPTO_ExchangeSignHelper *esh;
     365              :       int ret;
     366              : 
     367            8 :       esh = TALER_CRYPTO_helper_esign_connect (cfg,
     368              :                                                "taler-exchange",
     369              :                                                &key_cb,
     370              :                                                NULL);
     371            8 :       if (NULL == esh)
     372              :       {
     373            0 :         GNUNET_break (0);
     374            0 :         exit (EXIT_FAILURE);
     375              :       }
     376            8 :       ret = perf_signing (esh,
     377              :                           "parallel");
     378            8 :       TALER_CRYPTO_helper_esign_disconnect (esh);
     379            8 :       exit (ret);
     380              :     }
     381              :   }
     382            9 :   for (unsigned int i = 0; i<NUM_CORES; i++)
     383              :   {
     384              :     int wstatus;
     385              : 
     386            8 :     GNUNET_assert (pids[i] ==
     387              :                    waitpid (pids[i],
     388              :                             &wstatus,
     389              :                             0));
     390              :   }
     391            1 :   duration = GNUNET_TIME_absolute_get_duration (start);
     392            1 :   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            1 :   return 0;
     398              : }
     399              : 
     400              : 
     401              : /**
     402              :  * Main entry point into the test logic with the helper already running.
     403              :  */
     404              : static int
     405            1 : run_test (void)
     406              : {
     407              :   struct GNUNET_CONFIGURATION_Handle *cfg;
     408              :   struct TALER_CRYPTO_ExchangeSignHelper *esh;
     409              :   int ret;
     410            1 :   struct timespec req = {
     411              :     .tv_nsec = 250000000
     412              :   };
     413              : 
     414            1 :   cfg = GNUNET_CONFIGURATION_create (TALER_EXCHANGE_project_data ());
     415            1 :   if (GNUNET_OK !=
     416            1 :       GNUNET_CONFIGURATION_load (cfg,
     417              :                                  "test_helper_eddsa.conf"))
     418              :   {
     419            0 :     GNUNET_break (0);
     420            0 :     return 77;
     421              :   }
     422              : 
     423              :   /* wait for helper to start and give us keys */
     424            1 :   fprintf (stderr, "Waiting for helper to start ... ");
     425            1 :   for (unsigned int i = 0; i<100; i++)
     426              :   {
     427            1 :     nanosleep (&req,
     428              :                NULL);
     429            1 :     esh = TALER_CRYPTO_helper_esign_connect (cfg,
     430              :                                              "taler-exchange",
     431              :                                              &key_cb,
     432              :                                              NULL);
     433            1 :     if (NULL != esh)
     434            1 :       break;
     435            0 :     fprintf (stderr, ".");
     436              :   }
     437            1 :   if (NULL == esh)
     438              :   {
     439            0 :     fprintf (stderr,
     440              :              "\nFAILED: timeout trying to connect to helper\n");
     441            0 :     GNUNET_CONFIGURATION_destroy (cfg);
     442            0 :     return 1;
     443              :   }
     444            1 :   if (0 == num_keys)
     445              :   {
     446            0 :     fprintf (stderr,
     447              :              "\nFAILED: no keys returned by helper\n");
     448            0 :     TALER_CRYPTO_helper_esign_disconnect (esh);
     449            0 :     esh = NULL;
     450            0 :     GNUNET_CONFIGURATION_destroy (cfg);
     451            0 :     return 1;
     452              :   }
     453            1 :   fprintf (stderr,
     454              :            " Done (%u keys)\n",
     455              :            num_keys);
     456            1 :   ret = 0;
     457            1 :   if (0 == ret)
     458            1 :     ret = test_revocation (esh);
     459            1 :   if (0 == ret)
     460            1 :     ret = test_signing (esh);
     461            1 :   if (0 == ret)
     462            1 :     ret = perf_signing (esh,
     463              :                         "sequential");
     464            1 :   if (NULL != esh)
     465              :   {
     466            1 :     TALER_CRYPTO_helper_esign_disconnect (esh);
     467            1 :     esh = NULL;
     468              :   }
     469            1 :   if (0 == ret)
     470            1 :     ret = par_signing (cfg);
     471              :   /* clean up our state */
     472           21 :   for (unsigned int i = 0; i<MAX_KEYS; i++)
     473           20 :     if (keys[i].valid)
     474              :     {
     475            0 :       keys[i].valid = false;
     476            0 :       GNUNET_assert (num_keys > 0);
     477            0 :       num_keys--;
     478              :     }
     479            1 :   GNUNET_CONFIGURATION_destroy (cfg);
     480            1 :   return ret;
     481              : }
     482              : 
     483              : 
     484              : int
     485            1 : 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            1 :   unsetenv ("XDG_DATA_HOME");
     498            1 :   unsetenv ("XDG_CONFIG_HOME");
     499            1 :   GNUNET_log_setup ("test-helper-eddsa",
     500              :                     "INFO",
     501              :                     NULL);
     502            1 :   libexec_dir = GNUNET_OS_installation_get_path (TALER_EXCHANGE_project_data (),
     503              :                                                  GNUNET_OS_IPK_BINDIR);
     504            1 :   GNUNET_asprintf (&binary_name,
     505              :                    "%s/%s",
     506              :                    libexec_dir,
     507              :                    "taler-exchange-secmod-eddsa");
     508            1 :   GNUNET_free (libexec_dir);
     509            1 :   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            1 :   if (NULL == helper)
     519              :   {
     520            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     521              :                               "exec",
     522              :                               binary_name);
     523            0 :     GNUNET_free (binary_name);
     524            0 :     return 77;
     525              :   }
     526            1 :   GNUNET_free (binary_name);
     527            1 :   ret = run_test ();
     528              : 
     529            1 :   GNUNET_OS_process_kill (helper,
     530              :                           SIGTERM);
     531            1 :   if (GNUNET_OK !=
     532            1 :       GNUNET_OS_process_wait_status (helper,
     533              :                                      &type,
     534              :                                      &code))
     535              :   {
     536            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     537              :                 "Helper process did not die voluntarily, killing hard\n");
     538            0 :     GNUNET_OS_process_kill (helper,
     539              :                             SIGKILL);
     540            0 :     ret = 4;
     541              :   }
     542            1 :   else if ( (GNUNET_OS_PROCESS_EXITED != type) ||
     543            1 :             (0 != code) )
     544              :   {
     545            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     546              :                 "Helper died with unexpected status %d/%d\n",
     547              :                 (int) type,
     548              :                 (int) code);
     549            0 :     ret = 5;
     550              :   }
     551            1 :   GNUNET_OS_process_destroy (helper);
     552            1 :   return ret;
     553              : }
     554              : 
     555              : 
     556              : /* end of test_helper_eddsa.c */
        

Generated by: LCOV version 2.0-1