LCOV - code coverage report
Current view: top level - util - test_helper_eddsa.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 139 178 78.1 %
Date: 2025-06-05 21:03:14 Functions: 7 7 100.0 %

          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 "platform.h"
      22             : #include "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          11 :     for (unsigned int i = 0; i<MAX_KEYS; i++)
     136          11 :       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         392 :   for (unsigned int i = 0; i<MAX_KEYS; i++)
     153         392 :     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          11 :     for (unsigned int j = 0; j < MAX_KEYS; j++)
     190             :     {
     191          11 :       if (! keys[j].valid)
     192           0 :         continue;
     193          11 :       if (0 != off)
     194             :       {
     195           8 :         off--;
     196           8 :         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_EccSignaturePurpose 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_EccSignaturePurpose 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 1.16