test_helper_cs.c (36616B)
1 /* 2 This file is part of TALER 3 (C) 2020, 2021, 2023 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_cs.c 18 * @brief Tests for CS crypto helper 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include "taler/taler_util.h" 23 24 /** 25 * Configuration has 1 minute duration and 5 minutes lookahead, but 26 * we do not get 'revocations' for expired keys. So this must be 27 * large enough to deal with key rotation during the runtime of 28 * the benchmark. 29 */ 30 #define MAX_KEYS 1024 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 */ 40 #define NUM_SIGN_TESTS 5 41 42 /** 43 * How many iterations of the successful signing test should we run 44 * during the benchmark phase? 45 */ 46 #define NUM_SIGN_PERFS 100 47 48 /** 49 * How many parallel clients should we use for the parallel 50 * benchmark? (> 500 may cause problems with the max open FD number limit). 51 */ 52 #define NUM_CORES 8 53 54 /** 55 * Number of keys currently in #keys. 56 */ 57 static unsigned int num_keys; 58 59 /** 60 * Keys currently managed by the helper. 61 */ 62 struct KeyData 63 { 64 /** 65 * Validity start point. 66 */ 67 struct GNUNET_TIME_Timestamp start_time; 68 69 /** 70 * Key expires for signing at @e start_time plus this value. 71 */ 72 struct GNUNET_TIME_Relative validity_duration; 73 74 /** 75 * Hash of the public key. 76 */ 77 struct TALER_CsPubHashP h_cs; 78 79 /** 80 * Full public key. 81 */ 82 struct TALER_DenominationPublicKey denom_pub; 83 84 /** 85 * Is this key currently valid? 86 */ 87 bool valid; 88 89 /** 90 * Did the test driver revoke this key? 91 */ 92 bool revoked; 93 }; 94 95 /** 96 * Array of all the keys we got from the helper. 97 */ 98 static struct KeyData keys[MAX_KEYS]; 99 100 101 /** 102 * Release memory occupied by #keys. 103 */ 104 static void 105 free_keys (void) 106 { 107 for (unsigned int i = 0; i<MAX_KEYS; i++) 108 if (keys[i].valid) 109 { 110 TALER_denom_pub_free (&keys[i].denom_pub); 111 keys[i].valid = false; 112 GNUNET_assert (num_keys > 0); 113 num_keys--; 114 } 115 } 116 117 118 /** 119 * Function called with information about available keys for signing. Usually 120 * only called once per key upon connect. Also called again in case a key is 121 * being revoked, in that case with an @a end_time of zero. Stores the keys 122 * status in #keys. 123 * 124 * @param cls closure, NULL 125 * @param section_name name of the denomination type in the configuration; 126 * NULL if the key has been revoked or purged 127 * @param start_time when does the key become available for signing; 128 * zero if the key has been revoked or purged 129 * @param validity_duration how long does the key remain available for signing; 130 * zero if the key has been revoked or purged 131 * @param h_cs hash of the @a denom_pub that is available (or was purged) 132 * @param bs_pub the public key itself, NULL if the key was revoked or purged 133 * @param sm_pub public key of the security module, NULL if the key was revoked or purged 134 * @param sm_sig signature from the security module, NULL if the key was revoked or purged 135 * The signature was already verified against @a sm_pub. 136 */ 137 static void 138 key_cb (void *cls, 139 const char *section_name, 140 struct GNUNET_TIME_Timestamp start_time, 141 struct GNUNET_TIME_Relative validity_duration, 142 const struct TALER_CsPubHashP *h_cs, 143 struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub, 144 const struct TALER_SecurityModulePublicKeyP *sm_pub, 145 const struct TALER_SecurityModuleSignatureP *sm_sig) 146 { 147 (void) cls; 148 (void) sm_pub; 149 (void) sm_sig; 150 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 151 "Key notification about key %s in `%s'\n", 152 GNUNET_h2s (&h_cs->hash), 153 section_name); 154 if (0 == validity_duration.rel_value_us) 155 { 156 bool found = false; 157 158 GNUNET_break (NULL == bs_pub); 159 GNUNET_break (NULL == section_name); 160 for (unsigned int i = 0; i<MAX_KEYS; i++) 161 if (0 == GNUNET_memcmp (h_cs, 162 &keys[i].h_cs)) 163 { 164 keys[i].valid = false; 165 keys[i].revoked = false; 166 TALER_denom_pub_free (&keys[i].denom_pub); 167 GNUNET_assert (num_keys > 0); 168 num_keys--; 169 found = true; 170 break; 171 } 172 if (! found) 173 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 174 "Error: helper announced expiration of unknown key!\n"); 175 176 return; 177 } 178 179 GNUNET_break (NULL != bs_pub); 180 for (unsigned int i = 0; i<MAX_KEYS; i++) 181 if (! keys[i].valid) 182 { 183 keys[i].valid = true; 184 keys[i].h_cs = *h_cs; 185 keys[i].start_time = start_time; 186 keys[i].validity_duration = validity_duration; 187 keys[i].denom_pub.bsign_pub_key 188 = GNUNET_CRYPTO_bsign_pub_incref (bs_pub); 189 num_keys++; 190 return; 191 } 192 /* too many keys! */ 193 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 194 "Error: received %d live keys from the service!\n", 195 MAX_KEYS + 1); 196 } 197 198 199 /** 200 * Test key revocation logic. 201 * 202 * @param dh handle to the helper 203 * @return 0 on success 204 */ 205 static int 206 test_revocation (struct TALER_CRYPTO_CsDenominationHelper *dh) 207 { 208 struct timespec req = { 209 .tv_nsec = 250000000 210 }; 211 212 for (unsigned int i = 0; i<NUM_REVOKES; i++) 213 { 214 uint32_t off; 215 216 off = GNUNET_CRYPTO_random_u32 (num_keys); 217 /* find index of key to revoke */ 218 for (unsigned int j = 0; j < MAX_KEYS; j++) 219 { 220 if (! keys[j].valid) 221 continue; 222 if (0 != off) 223 { 224 off--; 225 continue; 226 } 227 keys[j].revoked = true; 228 fprintf (stderr, 229 "Revoking key %s ...", 230 GNUNET_h2s (&keys[j].h_cs.hash)); 231 TALER_CRYPTO_helper_cs_revoke (dh, 232 &keys[j].h_cs); 233 for (unsigned int k = 0; k<1000; k++) 234 { 235 TALER_CRYPTO_helper_cs_poll (dh); 236 if (! keys[j].revoked) 237 break; 238 nanosleep (&req, NULL); 239 fprintf (stderr, "."); 240 } 241 if (keys[j].revoked) 242 { 243 fprintf (stderr, 244 "\nFAILED: timeout trying to revoke key %u\n", 245 j); 246 TALER_CRYPTO_helper_cs_disconnect (dh); 247 return 2; 248 } 249 fprintf (stderr, "\n"); 250 break; 251 } 252 } 253 return 0; 254 } 255 256 257 /** 258 * Test R derivation logic. 259 * 260 * @param dh handle to the helper 261 * @return 0 on success 262 */ 263 static int 264 test_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh) 265 { 266 enum TALER_ErrorCode ec; 267 bool success = false; 268 struct TALER_PlanchetMasterSecretP ps; 269 struct TALER_CoinSpendPrivateKeyP coin_priv; 270 union GNUNET_CRYPTO_BlindingSecretP bks; 271 struct TALER_CoinPubHashP c_hash; 272 struct GNUNET_CRYPTO_BlindingInputValues bi = { 273 .cipher = GNUNET_CRYPTO_BSA_CS 274 }; 275 struct TALER_ExchangeBlindingValues alg_values = { 276 .blinding_inputs = &bi 277 }; 278 union GNUNET_CRYPTO_BlindSessionNonce nonce; 279 280 TALER_planchet_master_setup_random (&ps); 281 for (unsigned int i = 0; i<MAX_KEYS; i++) 282 { 283 struct TALER_PlanchetDetail pd; 284 285 if (! keys[i].valid) 286 continue; 287 GNUNET_assert (GNUNET_CRYPTO_BSA_CS == 288 keys[i].denom_pub.bsign_pub_key->cipher); 289 #pragma message "phase out TALER_cs_withdraw_nonce_derive" 290 TALER_cs_withdraw_nonce_derive ( 291 &ps, 292 &nonce.cs_nonce); 293 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 294 "Requesting R derivation with key %s\n", 295 GNUNET_h2s (&keys[i].h_cs.hash)); 296 { 297 struct TALER_CRYPTO_CsDeriveRequest cdr = { 298 .h_cs = &keys[i].h_cs, 299 .nonce = &nonce.cs_nonce 300 }; 301 302 ec = TALER_CRYPTO_helper_cs_r_batch_derive ( 303 dh, 304 1, 305 &cdr, 306 false, 307 &bi.details.cs_values); 308 } 309 switch (ec) 310 { 311 case TALER_EC_NONE: 312 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining ( 313 keys[i].start_time.abs_time), 314 >, 315 GNUNET_TIME_UNIT_SECONDS)) 316 { 317 /* key worked too early */ 318 GNUNET_break (0); 319 return 4; 320 } 321 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration ( 322 keys[i].start_time.abs_time), 323 >, 324 keys[i].validity_duration)) 325 { 326 /* key worked too later */ 327 GNUNET_break (0); 328 return 5; 329 } 330 331 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 332 "Received valid R for key %s\n", 333 GNUNET_h2s (&keys[i].h_cs.hash)); 334 TALER_planchet_setup_coin_priv (&ps, 335 &alg_values, 336 &coin_priv); 337 TALER_planchet_blinding_secret_create (&ps, 338 &alg_values, 339 &bks); 340 GNUNET_assert (GNUNET_OK == 341 TALER_planchet_prepare (&keys[i].denom_pub, 342 &alg_values, 343 &bks, 344 &nonce, 345 &coin_priv, 346 NULL, /* no age commitment */ 347 &c_hash, 348 &pd)); 349 TALER_blinded_planchet_free (&pd.blinded_planchet); 350 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 351 "Successfully prepared planchet"); 352 success = true; 353 break; 354 case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY: 355 /* This 'failure' is expected, we're testing also for the 356 error handling! */ 357 if ( (GNUNET_TIME_relative_is_zero ( 358 GNUNET_TIME_absolute_get_remaining ( 359 keys[i].start_time.abs_time))) && 360 (GNUNET_TIME_relative_cmp ( 361 GNUNET_TIME_absolute_get_duration ( 362 keys[i].start_time.abs_time), 363 <, 364 keys[i].validity_duration)) ) 365 { 366 /* key should have worked! */ 367 GNUNET_break (0); 368 return 6; 369 } 370 break; 371 default: 372 /* unexpected error */ 373 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 374 "Unexpected error %d\n", 375 ec); 376 return 7; 377 } 378 } 379 if (! success) 380 { 381 /* no valid key for signing found, also bad */ 382 GNUNET_break (0); 383 return 16; 384 } 385 386 /* check R derivation does not work if the key is unknown */ 387 { 388 struct TALER_CsPubHashP rnd; 389 struct GNUNET_CRYPTO_CSPublicRPairP crp; 390 struct TALER_CRYPTO_CsDeriveRequest cdr = { 391 .h_cs = &rnd, 392 .nonce = &nonce.cs_nonce, 393 }; 394 395 GNUNET_CRYPTO_random_block (&rnd, 396 sizeof (rnd)); 397 GNUNET_CRYPTO_random_block (&nonce, 398 sizeof (nonce)); 399 ec = TALER_CRYPTO_helper_cs_r_batch_derive (dh, 400 1, 401 &cdr, 402 false, 403 &crp); 404 if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec) 405 { 406 GNUNET_break (0); 407 return 17; 408 } 409 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 410 "R derivation with invalid key %s failed as desired\n", 411 GNUNET_h2s (&rnd.hash)); 412 } 413 return 0; 414 } 415 416 417 /** 418 * Test signing logic. 419 * 420 * @param dh handle to the helper 421 * @return 0 on success 422 */ 423 static int 424 test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh) 425 { 426 struct TALER_BlindedDenominationSignature ds; 427 enum TALER_ErrorCode ec; 428 bool success = false; 429 struct TALER_PlanchetMasterSecretP ps; 430 struct TALER_CoinSpendPrivateKeyP coin_priv; 431 union GNUNET_CRYPTO_BlindingSecretP bks; 432 struct TALER_CoinPubHashP c_hash; 433 struct GNUNET_CRYPTO_BlindingInputValues bi = { 434 .cipher = GNUNET_CRYPTO_BSA_CS 435 }; 436 struct TALER_ExchangeBlindingValues alg_values = { 437 .blinding_inputs = &bi 438 }; 439 union GNUNET_CRYPTO_BlindSessionNonce nonce; 440 441 TALER_planchet_master_setup_random (&ps); 442 for (unsigned int i = 0; i<MAX_KEYS; i++) 443 { 444 if (! keys[i].valid) 445 continue; 446 { 447 struct TALER_PlanchetDetail pd; 448 struct TALER_CRYPTO_CsSignRequest csr; 449 struct TALER_CRYPTO_CsDeriveRequest cdr = { 450 .h_cs = &keys[i].h_cs, 451 .nonce = &nonce.cs_nonce 452 }; 453 454 TALER_cs_withdraw_nonce_derive (&ps, 455 &nonce.cs_nonce); 456 ec = TALER_CRYPTO_helper_cs_r_batch_derive ( 457 dh, 458 1, 459 &cdr, 460 false, 461 &bi.details.cs_values); 462 if (TALER_EC_NONE != ec) 463 continue; 464 TALER_planchet_setup_coin_priv (&ps, 465 &alg_values, 466 &coin_priv); 467 TALER_planchet_blinding_secret_create (&ps, 468 &alg_values, 469 &bks); 470 GNUNET_assert (GNUNET_YES == 471 TALER_planchet_prepare (&keys[i].denom_pub, 472 &alg_values, 473 &bks, 474 &nonce, 475 &coin_priv, 476 NULL, /* no age commitment */ 477 &c_hash, 478 &pd)); 479 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 480 "Requesting signature with key %s\n", 481 GNUNET_h2s (&keys[i].h_cs.hash)); 482 csr.h_cs = &keys[i].h_cs; 483 csr.blinded_planchet 484 = &pd.blinded_planchet.blinded_message->details.cs_blinded_message; 485 ec = TALER_CRYPTO_helper_cs_batch_sign ( 486 dh, 487 1, 488 &csr, 489 false, 490 &ds); 491 TALER_blinded_planchet_free (&pd.blinded_planchet); 492 } 493 switch (ec) 494 { 495 case TALER_EC_NONE: 496 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining ( 497 keys[i].start_time.abs_time), 498 >, 499 GNUNET_TIME_UNIT_SECONDS)) 500 { 501 /* key worked too early */ 502 GNUNET_break (0); 503 TALER_blinded_denom_sig_free (&ds); 504 return 4; 505 } 506 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration ( 507 keys[i].start_time.abs_time), 508 >, 509 keys[i].validity_duration)) 510 { 511 /* key worked too later */ 512 GNUNET_break (0); 513 TALER_blinded_denom_sig_free (&ds); 514 return 5; 515 } 516 { 517 struct TALER_FreshCoin coin; 518 519 if (GNUNET_OK != 520 TALER_planchet_to_coin (&keys[i].denom_pub, 521 &ds, 522 &bks, 523 &coin_priv, 524 NULL, /* no age commitment */ 525 &c_hash, 526 &alg_values, 527 &coin)) 528 { 529 GNUNET_break (0); 530 TALER_blinded_denom_sig_free (&ds); 531 return 6; 532 } 533 TALER_blinded_denom_sig_free (&ds); 534 TALER_denom_sig_free (&coin.sig); 535 } 536 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 537 "Received valid signature for key %s\n", 538 GNUNET_h2s (&keys[i].h_cs.hash)); 539 success = true; 540 break; 541 case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY: 542 /* This 'failure' is expected, we're testing also for the 543 error handling! */ 544 if ( (GNUNET_TIME_relative_is_zero ( 545 GNUNET_TIME_absolute_get_remaining ( 546 keys[i].start_time.abs_time))) && 547 (GNUNET_TIME_relative_cmp ( 548 GNUNET_TIME_absolute_get_duration ( 549 keys[i].start_time.abs_time), 550 <, 551 keys[i].validity_duration)) ) 552 { 553 /* key should have worked! */ 554 GNUNET_break (0); 555 return 6; 556 } 557 break; 558 default: 559 /* unexpected error */ 560 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 561 "Unexpected error %d\n", 562 ec); 563 return 7; 564 } 565 } 566 if (! success) 567 { 568 /* no valid key for signing found, also bad */ 569 GNUNET_break (0); 570 return 16; 571 } 572 573 /* check signing does not work if the key is unknown */ 574 { 575 struct TALER_PlanchetDetail pd; 576 struct TALER_CsPubHashP rnd; 577 struct TALER_CRYPTO_CsSignRequest csr; 578 579 GNUNET_CRYPTO_random_block (&rnd, 580 sizeof (rnd)); 581 GNUNET_assert (GNUNET_YES == 582 TALER_planchet_prepare (&keys[0].denom_pub, 583 &alg_values, 584 &bks, 585 &nonce, 586 &coin_priv, 587 NULL, /* no age commitment */ 588 &c_hash, 589 &pd)); 590 csr.h_cs = &rnd; 591 csr.blinded_planchet 592 = &pd.blinded_planchet.blinded_message->details.cs_blinded_message; 593 ec = TALER_CRYPTO_helper_cs_batch_sign ( 594 dh, 595 1, 596 &csr, 597 false, 598 &ds); 599 TALER_blinded_planchet_free (&pd.blinded_planchet); 600 if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec) 601 { 602 if (TALER_EC_NONE == ec) 603 TALER_blinded_denom_sig_free (&ds); 604 GNUNET_break (0); 605 return 17; 606 } 607 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 608 "Signing with invalid key %s failed as desired\n", 609 GNUNET_h2s (&rnd.hash)); 610 } 611 return 0; 612 } 613 614 615 /** 616 * Test batch signing logic. 617 * 618 * @param dh handle to the helper 619 * @param batch_size how large should the batch be 620 * @param check_sigs also check unknown key and signatures 621 * @return 0 on success 622 */ 623 static int 624 test_batch_signing (struct TALER_CRYPTO_CsDenominationHelper *dh, 625 unsigned int batch_size, 626 bool check_sigs) 627 { 628 struct TALER_BlindedDenominationSignature ds[batch_size]; 629 enum TALER_ErrorCode ec; 630 bool success = false; 631 struct TALER_PlanchetMasterSecretP ps[batch_size]; 632 struct TALER_CoinSpendPrivateKeyP coin_priv[batch_size]; 633 union GNUNET_CRYPTO_BlindingSecretP bks[batch_size]; 634 struct TALER_CoinPubHashP c_hash[batch_size]; 635 struct GNUNET_CRYPTO_BlindingInputValues bi[batch_size]; 636 struct TALER_ExchangeBlindingValues alg_values[batch_size]; 637 union GNUNET_CRYPTO_BlindSessionNonce nonces[batch_size]; 638 639 for (unsigned int i = 0; i<batch_size; i++) 640 TALER_planchet_master_setup_random (&ps[i]); 641 for (unsigned int k = 0; k<MAX_KEYS; k++) 642 { 643 if (! keys[k].valid) 644 continue; 645 { 646 struct TALER_PlanchetDetail pd[batch_size]; 647 struct TALER_CRYPTO_CsSignRequest csr[batch_size]; 648 struct TALER_CRYPTO_CsDeriveRequest cdr[batch_size]; 649 struct GNUNET_CRYPTO_CSPublicRPairP crps[batch_size]; 650 651 for (unsigned int i = 0; i<batch_size; i++) 652 { 653 cdr[i].h_cs = &keys[k].h_cs; 654 cdr[i].nonce = &nonces[i].cs_nonce; 655 TALER_cs_withdraw_nonce_derive ( 656 &ps[i], 657 &nonces[i].cs_nonce); 658 bi[i].cipher = GNUNET_CRYPTO_BSA_CS; 659 alg_values[i].blinding_inputs = &bi[i]; 660 } 661 ec = TALER_CRYPTO_helper_cs_r_batch_derive ( 662 dh, 663 batch_size, 664 cdr, 665 false, 666 crps); 667 if (TALER_EC_NONE != ec) 668 continue; 669 for (unsigned int i = 0; i<batch_size; i++) 670 { 671 bi[i].details.cs_values = crps[i]; 672 TALER_planchet_setup_coin_priv (&ps[i], 673 &alg_values[i], 674 &coin_priv[i]); 675 TALER_planchet_blinding_secret_create (&ps[i], 676 &alg_values[i], 677 &bks[i]); 678 GNUNET_assert (GNUNET_YES == 679 TALER_planchet_prepare (&keys[k].denom_pub, 680 &alg_values[i], 681 &bks[i], 682 &nonces[i], 683 &coin_priv[i], 684 NULL, /* no age commitment */ 685 &c_hash[i], 686 &pd[i])); 687 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 688 "Requesting signature with key %s\n", 689 GNUNET_h2s (&keys[k].h_cs.hash)); 690 csr[i].h_cs = &keys[k].h_cs; 691 csr[i].blinded_planchet 692 = &pd[i].blinded_planchet.blinded_message->details.cs_blinded_message; 693 } 694 ec = TALER_CRYPTO_helper_cs_batch_sign ( 695 dh, 696 batch_size, 697 csr, 698 false, 699 ds); 700 for (unsigned int i = 0; i<batch_size; i++) 701 { 702 TALER_blinded_planchet_free (&pd[i].blinded_planchet); 703 } 704 } 705 switch (ec) 706 { 707 case TALER_EC_NONE: 708 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining ( 709 keys[k].start_time.abs_time), 710 >, 711 GNUNET_TIME_UNIT_SECONDS)) 712 { 713 /* key worked too early */ 714 GNUNET_break (0); 715 return 4; 716 } 717 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration ( 718 keys[k].start_time.abs_time), 719 >, 720 keys[k].validity_duration)) 721 { 722 /* key worked too later */ 723 GNUNET_break (0); 724 return 5; 725 } 726 if (check_sigs) 727 { 728 for (unsigned int i = 0; i<batch_size; i++) 729 { 730 struct TALER_FreshCoin coin; 731 732 if (GNUNET_OK != 733 TALER_planchet_to_coin (&keys[k].denom_pub, 734 &ds[i], 735 &bks[i], 736 &coin_priv[i], 737 NULL, /* no age commitment */ 738 &c_hash[i], 739 &alg_values[i], 740 &coin)) 741 { 742 GNUNET_break (0); 743 return 6; 744 } 745 TALER_blinded_denom_sig_free (&ds[i]); 746 TALER_denom_sig_free (&coin.sig); 747 } 748 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 749 "Received valid signature for key %s\n", 750 GNUNET_h2s (&keys[k].h_cs.hash)); 751 } 752 else 753 { 754 for (unsigned int i = 0; i<batch_size; i++) 755 TALER_blinded_denom_sig_free (&ds[i]); 756 } 757 success = true; 758 break; 759 case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY: 760 /* This 'failure' is expected, we're testing also for the 761 error handling! */ 762 if ( (GNUNET_TIME_relative_is_zero ( 763 GNUNET_TIME_absolute_get_remaining ( 764 keys[k].start_time.abs_time))) && 765 (GNUNET_TIME_relative_cmp ( 766 GNUNET_TIME_absolute_get_duration ( 767 keys[k].start_time.abs_time), 768 <, 769 keys[k].validity_duration)) ) 770 { 771 /* key should have worked! */ 772 GNUNET_break (0); 773 return 6; 774 } 775 break; 776 default: 777 /* unexpected error */ 778 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 779 "Unexpected error %d\n", 780 ec); 781 return 7; 782 } 783 } 784 if (! success) 785 { 786 /* no valid key for signing found, also bad */ 787 GNUNET_break (0); 788 return 16; 789 } 790 791 /* check signing does not work if the key is unknown */ 792 if (check_sigs) 793 { 794 struct TALER_PlanchetDetail pd; 795 struct TALER_CsPubHashP rnd; 796 struct TALER_CRYPTO_CsSignRequest csr; 797 798 GNUNET_CRYPTO_random_block (&rnd, 799 sizeof (rnd)); 800 GNUNET_assert (GNUNET_YES == 801 TALER_planchet_prepare (&keys[0].denom_pub, 802 &alg_values[0], 803 &bks[0], 804 &nonces[0], 805 &coin_priv[0], 806 NULL, /* no age commitment */ 807 &c_hash[0], 808 &pd)); 809 csr.h_cs = &rnd; 810 csr.blinded_planchet 811 = &pd.blinded_planchet.blinded_message->details.cs_blinded_message; 812 ec = TALER_CRYPTO_helper_cs_batch_sign ( 813 dh, 814 1, 815 &csr, 816 false, 817 &ds[0]); 818 TALER_blinded_planchet_free (&pd.blinded_planchet); 819 if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec) 820 { 821 if (TALER_EC_NONE == ec) 822 TALER_blinded_denom_sig_free (&ds[0]); 823 GNUNET_break (0); 824 return 17; 825 } 826 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 827 "Signing with invalid key %s failed as desired\n", 828 GNUNET_h2s (&rnd.hash)); 829 } 830 return 0; 831 } 832 833 834 /** 835 * Benchmark signing logic. 836 * 837 * @param dh handle to the helper 838 * @return 0 on success 839 */ 840 static int 841 perf_signing (struct TALER_CRYPTO_CsDenominationHelper *dh, 842 const char *type) 843 { 844 struct TALER_BlindedDenominationSignature ds; 845 enum TALER_ErrorCode ec; 846 struct GNUNET_TIME_Relative duration; 847 struct TALER_PlanchetMasterSecretP ps; 848 struct TALER_CoinSpendPrivateKeyP coin_priv; 849 union GNUNET_CRYPTO_BlindingSecretP bks; 850 struct GNUNET_CRYPTO_BlindingInputValues bv = { 851 .cipher = GNUNET_CRYPTO_BSA_CS 852 }; 853 struct TALER_ExchangeBlindingValues alg_values = { 854 .blinding_inputs = &bv 855 }; 856 857 TALER_planchet_master_setup_random (&ps); 858 duration = GNUNET_TIME_UNIT_ZERO; 859 TALER_CRYPTO_helper_cs_poll (dh); 860 for (unsigned int j = 0; j<NUM_SIGN_PERFS;) 861 { 862 for (unsigned int i = 0; i<MAX_KEYS; i++) 863 { 864 if (! keys[i].valid) 865 continue; 866 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining ( 867 keys[i].start_time.abs_time), 868 >, 869 GNUNET_TIME_UNIT_SECONDS)) 870 continue; 871 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration ( 872 keys[i].start_time.abs_time), 873 >, 874 keys[i].validity_duration)) 875 continue; 876 { 877 struct TALER_CoinPubHashP c_hash; 878 struct TALER_PlanchetDetail pd; 879 union GNUNET_CRYPTO_BlindSessionNonce nonce; 880 struct TALER_CRYPTO_CsDeriveRequest cdr = { 881 .h_cs = &keys[i].h_cs, 882 .nonce = &nonce.cs_nonce 883 }; 884 885 TALER_cs_withdraw_nonce_derive ( 886 &ps, 887 &nonce.cs_nonce); 888 ec = TALER_CRYPTO_helper_cs_r_batch_derive ( 889 dh, 890 1, 891 &cdr, 892 true, 893 &bv.details.cs_values); 894 if (TALER_EC_NONE != ec) 895 continue; 896 TALER_planchet_setup_coin_priv (&ps, 897 &alg_values, 898 &coin_priv); 899 TALER_planchet_blinding_secret_create (&ps, 900 &alg_values, 901 &bks); 902 GNUNET_assert (GNUNET_YES == 903 TALER_planchet_prepare (&keys[i].denom_pub, 904 &alg_values, 905 &bks, 906 &nonce, 907 &coin_priv, 908 NULL, /* no age commitment */ 909 &c_hash, 910 &pd)); 911 /* use this key as long as it works */ 912 while (1) 913 { 914 struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get (); 915 struct GNUNET_TIME_Relative delay; 916 struct TALER_CRYPTO_CsSignRequest csr; 917 918 csr.h_cs = &keys[i].h_cs; 919 csr.blinded_planchet 920 = &pd.blinded_planchet.blinded_message->details.cs_blinded_message; 921 ec = TALER_CRYPTO_helper_cs_batch_sign ( 922 dh, 923 1, 924 &csr, 925 true, 926 &ds); 927 if (TALER_EC_NONE != ec) 928 break; 929 delay = GNUNET_TIME_absolute_get_duration (start); 930 duration = GNUNET_TIME_relative_add (duration, 931 delay); 932 TALER_blinded_denom_sig_free (&ds); 933 j++; 934 if (NUM_SIGN_PERFS <= j) 935 break; 936 } 937 TALER_blinded_planchet_free (&pd.blinded_planchet); 938 } 939 } /* for i */ 940 } /* for j */ 941 fprintf (stderr, 942 "%u (%s) signature operations took %s\n", 943 (unsigned int) NUM_SIGN_PERFS, 944 type, 945 GNUNET_STRINGS_relative_time_to_string (duration, 946 GNUNET_YES)); 947 return 0; 948 } 949 950 951 /** 952 * Parallel signing logic. 953 * 954 * @param esh handle to the helper 955 * @return 0 on success 956 */ 957 static int 958 par_signing (struct GNUNET_CONFIGURATION_Handle *cfg) 959 { 960 struct GNUNET_TIME_Absolute start; 961 struct GNUNET_TIME_Relative duration; 962 pid_t pids[NUM_CORES]; 963 struct TALER_CRYPTO_CsDenominationHelper *dh; 964 965 start = GNUNET_TIME_absolute_get (); 966 for (unsigned int i = 0; i<NUM_CORES; i++) 967 { 968 pids[i] = fork (); 969 num_keys = 0; 970 GNUNET_assert (-1 != pids[i]); 971 if (0 == pids[i]) 972 { 973 int ret; 974 975 dh = TALER_CRYPTO_helper_cs_connect (cfg, 976 "taler-exchange", 977 &key_cb, 978 NULL); 979 GNUNET_assert (NULL != dh); 980 ret = perf_signing (dh, 981 "parallel"); 982 TALER_CRYPTO_helper_cs_disconnect (dh); 983 free_keys (); 984 exit (ret); 985 } 986 } 987 for (unsigned int i = 0; i<NUM_CORES; i++) 988 { 989 int wstatus; 990 991 GNUNET_assert (pids[i] == 992 waitpid (pids[i], 993 &wstatus, 994 0)); 995 } 996 duration = GNUNET_TIME_absolute_get_duration (start); 997 fprintf (stderr, 998 "%u (parallel) signature operations took %s (total real time)\n", 999 (unsigned int) NUM_SIGN_PERFS * NUM_CORES, 1000 GNUNET_STRINGS_relative_time_to_string (duration, 1001 GNUNET_YES)); 1002 return 0; 1003 } 1004 1005 1006 /** 1007 * Main entry point into the test logic with the helper already running. 1008 */ 1009 static int 1010 run_test (void) 1011 { 1012 struct GNUNET_CONFIGURATION_Handle *cfg; 1013 struct TALER_CRYPTO_CsDenominationHelper *dh; 1014 struct timespec req = { 1015 .tv_nsec = 250000000 1016 }; 1017 int ret; 1018 1019 cfg = GNUNET_CONFIGURATION_create (TALER_EXCHANGE_project_data ()); 1020 if (GNUNET_OK != 1021 GNUNET_CONFIGURATION_load (cfg, 1022 "test_helper_cs.conf")) 1023 { 1024 GNUNET_break (0); 1025 return 77; 1026 } 1027 1028 fprintf (stderr, "Waiting for helper to start ... "); 1029 for (unsigned int i = 0; i<100; i++) 1030 { 1031 nanosleep (&req, 1032 NULL); 1033 dh = TALER_CRYPTO_helper_cs_connect (cfg, 1034 "taler-exchange", 1035 &key_cb, 1036 NULL); 1037 if (NULL != dh) 1038 break; 1039 fprintf (stderr, "."); 1040 } 1041 if (NULL == dh) 1042 { 1043 fprintf (stderr, 1044 "\nFAILED: timeout trying to connect to helper\n"); 1045 GNUNET_CONFIGURATION_destroy (cfg); 1046 return 1; 1047 } 1048 if (0 == num_keys) 1049 { 1050 fprintf (stderr, 1051 "\nFAILED: timeout trying to connect to helper\n"); 1052 TALER_CRYPTO_helper_cs_disconnect (dh); 1053 GNUNET_CONFIGURATION_destroy (cfg); 1054 return 1; 1055 } 1056 fprintf (stderr, 1057 " Done (%u keys)\n", 1058 num_keys); 1059 ret = 0; 1060 if (0 == ret) 1061 ret = test_revocation (dh); 1062 if (0 == ret) 1063 ret = test_r_derive (dh); 1064 if (0 == ret) 1065 ret = test_signing (dh); 1066 if (0 == ret) 1067 ret = test_batch_signing (dh, 1068 2, 1069 true); 1070 if (0 == ret) 1071 ret = test_batch_signing (dh, 1072 64, 1073 true); 1074 for (unsigned int i = 0; i<4; i++) 1075 { 1076 static unsigned int batches[] = { 1, 4, 16, 64 }; 1077 unsigned int batch_size = batches[i]; 1078 struct GNUNET_TIME_Absolute start; 1079 struct GNUNET_TIME_Relative duration; 1080 1081 start = GNUNET_TIME_absolute_get (); 1082 if (0 != ret) 1083 break; 1084 ret = test_batch_signing (dh, 1085 batch_size, 1086 false); 1087 duration = GNUNET_TIME_absolute_get_duration (start); 1088 fprintf (stderr, 1089 "%4u (batch) signature operations took %s (total real time)\n", 1090 (unsigned int) batch_size, 1091 GNUNET_STRINGS_relative_time_to_string (duration, 1092 GNUNET_YES)); 1093 } 1094 if (0 == ret) 1095 ret = perf_signing (dh, 1096 "sequential"); 1097 TALER_CRYPTO_helper_cs_disconnect (dh); 1098 free_keys (); 1099 if (0 == ret) 1100 ret = par_signing (cfg); 1101 /* clean up our state */ 1102 GNUNET_CONFIGURATION_destroy (cfg); 1103 return ret; 1104 } 1105 1106 1107 int 1108 main (int argc, 1109 const char *const argv[]) 1110 { 1111 struct GNUNET_Process *helper; 1112 char *libexec_dir; 1113 char *binary_name; 1114 int ret; 1115 enum GNUNET_OS_ProcessStatusType type; 1116 unsigned long code; 1117 const char *loglev = "WARNING"; 1118 1119 (void) argc; 1120 (void) argv; 1121 unsetenv ("XDG_DATA_HOME"); 1122 unsetenv ("XDG_CONFIG_HOME"); 1123 GNUNET_log_setup ("test-helper-cs", 1124 loglev, 1125 NULL); 1126 libexec_dir = GNUNET_OS_installation_get_path (TALER_EXCHANGE_project_data (), 1127 GNUNET_OS_IPK_BINDIR); 1128 GNUNET_asprintf (&binary_name, 1129 "%s/%s", 1130 libexec_dir, 1131 "taler-exchange-secmod-cs"); 1132 GNUNET_free (libexec_dir); 1133 helper = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR); 1134 if (GNUNET_OK != 1135 GNUNET_process_run_command_va (helper, 1136 binary_name, 1137 binary_name, 1138 "-c", 1139 "test_helper_cs.conf", 1140 "-L", 1141 loglev, 1142 NULL)) 1143 { 1144 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 1145 "exec", 1146 binary_name); 1147 GNUNET_process_destroy (helper); 1148 GNUNET_free (binary_name); 1149 return 77; 1150 } 1151 GNUNET_free (binary_name); 1152 ret = run_test (); 1153 1154 GNUNET_break (GNUNET_OK == 1155 GNUNET_process_kill (helper, 1156 SIGTERM)); 1157 if (GNUNET_OK != 1158 GNUNET_process_wait (helper, 1159 true, 1160 &type, 1161 &code)) 1162 { 1163 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1164 "Helper process did not die voluntarily, killing hard\n"); 1165 GNUNET_break (GNUNET_OK == 1166 GNUNET_process_kill (helper, 1167 SIGKILL)); 1168 ret = 4; 1169 } 1170 else if ( (GNUNET_OS_PROCESS_EXITED != type) || 1171 (0 != code) ) 1172 { 1173 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1174 "Helper died with unexpected status %d/%d\n", 1175 (int) type, 1176 (int) code); 1177 ret = 5; 1178 } 1179 GNUNET_process_destroy (helper); 1180 return ret; 1181 } 1182 1183 1184 /* end of test_helper_cs.c */