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 */