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