test_helper_cs.c (37569B)
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 * Set up planchet secrets and Clause-Schnorr nonces for a withdraw-like 259 * operation. 260 * 261 * @param num number of planchets and nonces to derive 262 * @param for_melt true to use the refresh nonce domain 263 * @param[out] ps planchet secrets to initialize 264 * @param[out] nonces Clause-Schnorr nonces to initialize 265 */ 266 static void 267 setup_withdraw_secrets ( 268 size_t num, 269 bool for_melt, 270 struct TALER_PlanchetMasterSecretP ps[static num], 271 union GNUNET_CRYPTO_BlindSessionNonce nonces[static num]) 272 { 273 struct TALER_WithdrawMasterSeedP seed; 274 struct TALER_BlindingMasterSeedP blinding_seed; 275 uint32_t indices[num]; 276 277 TALER_withdraw_master_seed_setup_random (&seed); 278 TALER_withdraw_expand_secrets (num, 279 &seed, 280 ps); 281 TALER_cs_withdraw_seed_to_blinding_seed (&seed, 282 &blinding_seed); 283 for (uint32_t i = 0; i<num; i++) 284 indices[i] = i; 285 TALER_cs_derive_only_cs_blind_nonces_from_seed ( 286 &blinding_seed, 287 for_melt, 288 num, 289 indices, 290 nonces); 291 } 292 293 294 /** 295 * Test R derivation logic. 296 * 297 * @param dh handle to the helper 298 * @return 0 on success 299 */ 300 static int 301 test_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh) 302 { 303 enum TALER_ErrorCode ec; 304 bool success = false; 305 struct TALER_PlanchetMasterSecretP ps; 306 struct TALER_CoinSpendPrivateKeyP coin_priv; 307 union GNUNET_CRYPTO_BlindingSecretP bks; 308 struct TALER_CoinPubHashP c_hash; 309 struct GNUNET_CRYPTO_BlindingInputValues bi = { 310 .cipher = GNUNET_CRYPTO_BSA_CS 311 }; 312 struct TALER_ExchangeBlindingValues alg_values = { 313 .blinding_inputs = &bi 314 }; 315 union GNUNET_CRYPTO_BlindSessionNonce nonce; 316 317 setup_withdraw_secrets (1, 318 false, 319 &ps, 320 &nonce); 321 for (unsigned int i = 0; i<MAX_KEYS; i++) 322 { 323 struct TALER_PlanchetDetail pd; 324 325 if (! keys[i].valid) 326 continue; 327 GNUNET_assert (GNUNET_CRYPTO_BSA_CS == 328 keys[i].denom_pub.bsign_pub_key->cipher); 329 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 330 "Requesting R derivation with key %s\n", 331 GNUNET_h2s (&keys[i].h_cs.hash)); 332 { 333 struct TALER_CRYPTO_CsDeriveRequest cdr = { 334 .h_cs = &keys[i].h_cs, 335 .nonce = &nonce.cs_nonce 336 }; 337 338 ec = TALER_CRYPTO_helper_cs_r_batch_derive ( 339 dh, 340 1, 341 &cdr, 342 false, 343 &bi.details.cs_values); 344 } 345 switch (ec) 346 { 347 case TALER_EC_NONE: 348 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining ( 349 keys[i].start_time.abs_time), 350 >, 351 GNUNET_TIME_UNIT_SECONDS)) 352 { 353 /* key worked too early */ 354 GNUNET_break (0); 355 return 4; 356 } 357 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration ( 358 keys[i].start_time.abs_time), 359 >, 360 keys[i].validity_duration)) 361 { 362 /* key worked too later */ 363 GNUNET_break (0); 364 return 5; 365 } 366 367 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 368 "Received valid R for key %s\n", 369 GNUNET_h2s (&keys[i].h_cs.hash)); 370 TALER_planchet_setup_coin_priv (&ps, 371 &alg_values, 372 &coin_priv); 373 TALER_planchet_blinding_secret_create (&ps, 374 &alg_values, 375 &bks); 376 GNUNET_assert (GNUNET_OK == 377 TALER_planchet_prepare (&keys[i].denom_pub, 378 &alg_values, 379 &bks, 380 &nonce, 381 &coin_priv, 382 NULL, /* no age commitment */ 383 &c_hash, 384 &pd)); 385 TALER_blinded_planchet_free (&pd.blinded_planchet); 386 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 387 "Successfully prepared planchet"); 388 success = true; 389 break; 390 case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY: 391 /* This 'failure' is expected, we're testing also for the 392 error handling! */ 393 if ( (GNUNET_TIME_relative_is_zero ( 394 GNUNET_TIME_absolute_get_remaining ( 395 keys[i].start_time.abs_time))) && 396 (GNUNET_TIME_relative_cmp ( 397 GNUNET_TIME_absolute_get_duration ( 398 keys[i].start_time.abs_time), 399 <, 400 keys[i].validity_duration)) ) 401 { 402 /* key should have worked! */ 403 GNUNET_break (0); 404 return 6; 405 } 406 break; 407 default: 408 /* unexpected error */ 409 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 410 "Unexpected error %d\n", 411 ec); 412 return 7; 413 } 414 } 415 if (! success) 416 { 417 /* no valid key for signing found, also bad */ 418 GNUNET_break (0); 419 return 16; 420 } 421 422 /* check R derivation does not work if the key is unknown */ 423 { 424 struct TALER_CsPubHashP rnd; 425 struct GNUNET_CRYPTO_CSPublicRPairP crp; 426 struct TALER_CRYPTO_CsDeriveRequest cdr = { 427 .h_cs = &rnd, 428 .nonce = &nonce.cs_nonce, 429 }; 430 431 GNUNET_CRYPTO_random_block (&rnd, 432 sizeof (rnd)); 433 GNUNET_CRYPTO_random_block (&nonce, 434 sizeof (nonce)); 435 ec = TALER_CRYPTO_helper_cs_r_batch_derive (dh, 436 1, 437 &cdr, 438 false, 439 &crp); 440 if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec) 441 { 442 GNUNET_break (0); 443 return 17; 444 } 445 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 446 "R derivation with invalid key %s failed as desired\n", 447 GNUNET_h2s (&rnd.hash)); 448 } 449 return 0; 450 } 451 452 453 /** 454 * Test signing logic. 455 * 456 * @param dh handle to the helper 457 * @return 0 on success 458 */ 459 static int 460 test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh) 461 { 462 struct TALER_BlindedDenominationSignature ds; 463 enum TALER_ErrorCode ec; 464 bool success = false; 465 struct TALER_PlanchetMasterSecretP ps; 466 struct TALER_CoinSpendPrivateKeyP coin_priv; 467 union GNUNET_CRYPTO_BlindingSecretP bks; 468 struct TALER_CoinPubHashP c_hash; 469 struct GNUNET_CRYPTO_BlindingInputValues bi = { 470 .cipher = GNUNET_CRYPTO_BSA_CS 471 }; 472 struct TALER_ExchangeBlindingValues alg_values = { 473 .blinding_inputs = &bi 474 }; 475 union GNUNET_CRYPTO_BlindSessionNonce nonce; 476 477 setup_withdraw_secrets (1, 478 false, 479 &ps, 480 &nonce); 481 for (unsigned int i = 0; i<MAX_KEYS; i++) 482 { 483 if (! keys[i].valid) 484 continue; 485 { 486 struct TALER_PlanchetDetail pd; 487 struct TALER_CRYPTO_CsSignRequest csr; 488 struct TALER_CRYPTO_CsDeriveRequest cdr = { 489 .h_cs = &keys[i].h_cs, 490 .nonce = &nonce.cs_nonce 491 }; 492 493 ec = TALER_CRYPTO_helper_cs_r_batch_derive ( 494 dh, 495 1, 496 &cdr, 497 false, 498 &bi.details.cs_values); 499 if (TALER_EC_NONE != ec) 500 continue; 501 TALER_planchet_setup_coin_priv (&ps, 502 &alg_values, 503 &coin_priv); 504 TALER_planchet_blinding_secret_create (&ps, 505 &alg_values, 506 &bks); 507 GNUNET_assert (GNUNET_YES == 508 TALER_planchet_prepare (&keys[i].denom_pub, 509 &alg_values, 510 &bks, 511 &nonce, 512 &coin_priv, 513 NULL, /* no age commitment */ 514 &c_hash, 515 &pd)); 516 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 517 "Requesting signature with key %s\n", 518 GNUNET_h2s (&keys[i].h_cs.hash)); 519 csr.h_cs = &keys[i].h_cs; 520 csr.blinded_planchet 521 = &pd.blinded_planchet.blinded_message->details.cs_blinded_message; 522 ec = TALER_CRYPTO_helper_cs_batch_sign ( 523 dh, 524 1, 525 &csr, 526 false, 527 &ds); 528 TALER_blinded_planchet_free (&pd.blinded_planchet); 529 } 530 switch (ec) 531 { 532 case TALER_EC_NONE: 533 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining ( 534 keys[i].start_time.abs_time), 535 >, 536 GNUNET_TIME_UNIT_SECONDS)) 537 { 538 /* key worked too early */ 539 GNUNET_break (0); 540 TALER_blinded_denom_sig_free (&ds); 541 return 4; 542 } 543 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration ( 544 keys[i].start_time.abs_time), 545 >, 546 keys[i].validity_duration)) 547 { 548 /* key worked too later */ 549 GNUNET_break (0); 550 TALER_blinded_denom_sig_free (&ds); 551 return 5; 552 } 553 { 554 struct TALER_FreshCoin coin; 555 556 if (GNUNET_OK != 557 TALER_planchet_to_coin (&keys[i].denom_pub, 558 &ds, 559 &bks, 560 &coin_priv, 561 NULL, /* no age commitment */ 562 &c_hash, 563 &alg_values, 564 &coin)) 565 { 566 GNUNET_break (0); 567 TALER_blinded_denom_sig_free (&ds); 568 return 6; 569 } 570 TALER_blinded_denom_sig_free (&ds); 571 TALER_denom_sig_free (&coin.sig); 572 } 573 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 574 "Received valid signature for key %s\n", 575 GNUNET_h2s (&keys[i].h_cs.hash)); 576 success = true; 577 break; 578 case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY: 579 /* This 'failure' is expected, we're testing also for the 580 error handling! */ 581 if ( (GNUNET_TIME_relative_is_zero ( 582 GNUNET_TIME_absolute_get_remaining ( 583 keys[i].start_time.abs_time))) && 584 (GNUNET_TIME_relative_cmp ( 585 GNUNET_TIME_absolute_get_duration ( 586 keys[i].start_time.abs_time), 587 <, 588 keys[i].validity_duration)) ) 589 { 590 /* key should have worked! */ 591 GNUNET_break (0); 592 return 6; 593 } 594 break; 595 default: 596 /* unexpected error */ 597 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 598 "Unexpected error %d\n", 599 ec); 600 return 7; 601 } 602 } 603 if (! success) 604 { 605 /* no valid key for signing found, also bad */ 606 GNUNET_break (0); 607 return 16; 608 } 609 610 /* check signing does not work if the key is unknown */ 611 { 612 struct TALER_PlanchetDetail pd; 613 struct TALER_CsPubHashP rnd; 614 struct TALER_CRYPTO_CsSignRequest csr; 615 616 GNUNET_CRYPTO_random_block (&rnd, 617 sizeof (rnd)); 618 GNUNET_assert (GNUNET_YES == 619 TALER_planchet_prepare (&keys[0].denom_pub, 620 &alg_values, 621 &bks, 622 &nonce, 623 &coin_priv, 624 NULL, /* no age commitment */ 625 &c_hash, 626 &pd)); 627 csr.h_cs = &rnd; 628 csr.blinded_planchet 629 = &pd.blinded_planchet.blinded_message->details.cs_blinded_message; 630 ec = TALER_CRYPTO_helper_cs_batch_sign ( 631 dh, 632 1, 633 &csr, 634 false, 635 &ds); 636 TALER_blinded_planchet_free (&pd.blinded_planchet); 637 if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec) 638 { 639 if (TALER_EC_NONE == ec) 640 TALER_blinded_denom_sig_free (&ds); 641 GNUNET_break (0); 642 return 17; 643 } 644 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 645 "Signing with invalid key %s failed as desired\n", 646 GNUNET_h2s (&rnd.hash)); 647 } 648 return 0; 649 } 650 651 652 /** 653 * Test batch signing logic. 654 * 655 * @param dh handle to the helper 656 * @param batch_size how large should the batch be 657 * @param check_sigs also check unknown key and signatures 658 * @return 0 on success 659 */ 660 static int 661 test_batch_signing (struct TALER_CRYPTO_CsDenominationHelper *dh, 662 unsigned int batch_size, 663 bool check_sigs) 664 { 665 struct TALER_BlindedDenominationSignature ds[batch_size]; 666 enum TALER_ErrorCode ec; 667 bool success = false; 668 struct TALER_PlanchetMasterSecretP ps[batch_size]; 669 struct TALER_CoinSpendPrivateKeyP coin_priv[batch_size]; 670 union GNUNET_CRYPTO_BlindingSecretP bks[batch_size]; 671 struct TALER_CoinPubHashP c_hash[batch_size]; 672 struct GNUNET_CRYPTO_BlindingInputValues bi[batch_size]; 673 struct TALER_ExchangeBlindingValues alg_values[batch_size]; 674 union GNUNET_CRYPTO_BlindSessionNonce nonces[batch_size]; 675 676 setup_withdraw_secrets (batch_size, 677 false, 678 ps, 679 nonces); 680 for (unsigned int k = 0; k<MAX_KEYS; k++) 681 { 682 if (! keys[k].valid) 683 continue; 684 { 685 struct TALER_PlanchetDetail pd[batch_size]; 686 struct TALER_CRYPTO_CsSignRequest csr[batch_size]; 687 struct TALER_CRYPTO_CsDeriveRequest cdr[batch_size]; 688 struct GNUNET_CRYPTO_CSPublicRPairP crps[batch_size]; 689 690 for (unsigned int i = 0; i<batch_size; i++) 691 { 692 cdr[i].h_cs = &keys[k].h_cs; 693 cdr[i].nonce = &nonces[i].cs_nonce; 694 bi[i].cipher = GNUNET_CRYPTO_BSA_CS; 695 alg_values[i].blinding_inputs = &bi[i]; 696 } 697 ec = TALER_CRYPTO_helper_cs_r_batch_derive ( 698 dh, 699 batch_size, 700 cdr, 701 false, 702 crps); 703 if (TALER_EC_NONE != ec) 704 continue; 705 for (unsigned int i = 0; i<batch_size; i++) 706 { 707 bi[i].details.cs_values = crps[i]; 708 TALER_planchet_setup_coin_priv (&ps[i], 709 &alg_values[i], 710 &coin_priv[i]); 711 TALER_planchet_blinding_secret_create (&ps[i], 712 &alg_values[i], 713 &bks[i]); 714 GNUNET_assert (GNUNET_YES == 715 TALER_planchet_prepare (&keys[k].denom_pub, 716 &alg_values[i], 717 &bks[i], 718 &nonces[i], 719 &coin_priv[i], 720 NULL, /* no age commitment */ 721 &c_hash[i], 722 &pd[i])); 723 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 724 "Requesting signature with key %s\n", 725 GNUNET_h2s (&keys[k].h_cs.hash)); 726 csr[i].h_cs = &keys[k].h_cs; 727 csr[i].blinded_planchet 728 = &pd[i].blinded_planchet.blinded_message->details.cs_blinded_message; 729 } 730 ec = TALER_CRYPTO_helper_cs_batch_sign ( 731 dh, 732 batch_size, 733 csr, 734 false, 735 ds); 736 for (unsigned int i = 0; i<batch_size; i++) 737 { 738 TALER_blinded_planchet_free (&pd[i].blinded_planchet); 739 } 740 } 741 switch (ec) 742 { 743 case TALER_EC_NONE: 744 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining ( 745 keys[k].start_time.abs_time), 746 >, 747 GNUNET_TIME_UNIT_SECONDS)) 748 { 749 /* key worked too early */ 750 GNUNET_break (0); 751 return 4; 752 } 753 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration ( 754 keys[k].start_time.abs_time), 755 >, 756 keys[k].validity_duration)) 757 { 758 /* key worked too later */ 759 GNUNET_break (0); 760 return 5; 761 } 762 if (check_sigs) 763 { 764 for (unsigned int i = 0; i<batch_size; i++) 765 { 766 struct TALER_FreshCoin coin; 767 768 if (GNUNET_OK != 769 TALER_planchet_to_coin (&keys[k].denom_pub, 770 &ds[i], 771 &bks[i], 772 &coin_priv[i], 773 NULL, /* no age commitment */ 774 &c_hash[i], 775 &alg_values[i], 776 &coin)) 777 { 778 GNUNET_break (0); 779 return 6; 780 } 781 TALER_blinded_denom_sig_free (&ds[i]); 782 TALER_denom_sig_free (&coin.sig); 783 } 784 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 785 "Received valid signature for key %s\n", 786 GNUNET_h2s (&keys[k].h_cs.hash)); 787 } 788 else 789 { 790 for (unsigned int i = 0; i<batch_size; i++) 791 TALER_blinded_denom_sig_free (&ds[i]); 792 } 793 success = true; 794 break; 795 case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY: 796 /* This 'failure' is expected, we're testing also for the 797 error handling! */ 798 if ( (GNUNET_TIME_relative_is_zero ( 799 GNUNET_TIME_absolute_get_remaining ( 800 keys[k].start_time.abs_time))) && 801 (GNUNET_TIME_relative_cmp ( 802 GNUNET_TIME_absolute_get_duration ( 803 keys[k].start_time.abs_time), 804 <, 805 keys[k].validity_duration)) ) 806 { 807 /* key should have worked! */ 808 GNUNET_break (0); 809 return 6; 810 } 811 break; 812 default: 813 /* unexpected error */ 814 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 815 "Unexpected error %d\n", 816 ec); 817 return 7; 818 } 819 } 820 if (! success) 821 { 822 /* no valid key for signing found, also bad */ 823 GNUNET_break (0); 824 return 16; 825 } 826 827 /* check signing does not work if the key is unknown */ 828 if (check_sigs) 829 { 830 struct TALER_PlanchetDetail pd; 831 struct TALER_CsPubHashP rnd; 832 struct TALER_CRYPTO_CsSignRequest csr; 833 834 GNUNET_CRYPTO_random_block (&rnd, 835 sizeof (rnd)); 836 GNUNET_assert (GNUNET_YES == 837 TALER_planchet_prepare (&keys[0].denom_pub, 838 &alg_values[0], 839 &bks[0], 840 &nonces[0], 841 &coin_priv[0], 842 NULL, /* no age commitment */ 843 &c_hash[0], 844 &pd)); 845 csr.h_cs = &rnd; 846 csr.blinded_planchet 847 = &pd.blinded_planchet.blinded_message->details.cs_blinded_message; 848 ec = TALER_CRYPTO_helper_cs_batch_sign ( 849 dh, 850 1, 851 &csr, 852 false, 853 &ds[0]); 854 TALER_blinded_planchet_free (&pd.blinded_planchet); 855 if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec) 856 { 857 if (TALER_EC_NONE == ec) 858 TALER_blinded_denom_sig_free (&ds[0]); 859 GNUNET_break (0); 860 return 17; 861 } 862 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 863 "Signing with invalid key %s failed as desired\n", 864 GNUNET_h2s (&rnd.hash)); 865 } 866 return 0; 867 } 868 869 870 /** 871 * Benchmark signing logic. 872 * 873 * @param dh handle to the helper 874 * @return 0 on success 875 */ 876 static int 877 perf_signing (struct TALER_CRYPTO_CsDenominationHelper *dh, 878 const char *type) 879 { 880 struct TALER_BlindedDenominationSignature ds; 881 enum TALER_ErrorCode ec; 882 struct GNUNET_TIME_Relative duration; 883 struct TALER_PlanchetMasterSecretP ps; 884 struct TALER_CoinSpendPrivateKeyP coin_priv; 885 union GNUNET_CRYPTO_BlindingSecretP bks; 886 struct GNUNET_CRYPTO_BlindingInputValues bv = { 887 .cipher = GNUNET_CRYPTO_BSA_CS 888 }; 889 struct TALER_ExchangeBlindingValues alg_values = { 890 .blinding_inputs = &bv 891 }; 892 union GNUNET_CRYPTO_BlindSessionNonce nonce; 893 894 setup_withdraw_secrets (1, 895 true, 896 &ps, 897 &nonce); 898 duration = GNUNET_TIME_UNIT_ZERO; 899 TALER_CRYPTO_helper_cs_poll (dh); 900 for (unsigned int j = 0; j<NUM_SIGN_PERFS;) 901 { 902 for (unsigned int i = 0; i<MAX_KEYS; i++) 903 { 904 if (! keys[i].valid) 905 continue; 906 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining ( 907 keys[i].start_time.abs_time), 908 >, 909 GNUNET_TIME_UNIT_SECONDS)) 910 continue; 911 if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration ( 912 keys[i].start_time.abs_time), 913 >, 914 keys[i].validity_duration)) 915 continue; 916 { 917 struct TALER_CoinPubHashP c_hash; 918 struct TALER_PlanchetDetail pd; 919 struct TALER_CRYPTO_CsDeriveRequest cdr = { 920 .h_cs = &keys[i].h_cs, 921 .nonce = &nonce.cs_nonce 922 }; 923 924 ec = TALER_CRYPTO_helper_cs_r_batch_derive ( 925 dh, 926 1, 927 &cdr, 928 true, 929 &bv.details.cs_values); 930 if (TALER_EC_NONE != ec) 931 continue; 932 TALER_planchet_setup_coin_priv (&ps, 933 &alg_values, 934 &coin_priv); 935 TALER_planchet_blinding_secret_create (&ps, 936 &alg_values, 937 &bks); 938 GNUNET_assert (GNUNET_YES == 939 TALER_planchet_prepare (&keys[i].denom_pub, 940 &alg_values, 941 &bks, 942 &nonce, 943 &coin_priv, 944 NULL, /* no age commitment */ 945 &c_hash, 946 &pd)); 947 /* use this key as long as it works */ 948 while (1) 949 { 950 struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get (); 951 struct GNUNET_TIME_Relative delay; 952 struct TALER_CRYPTO_CsSignRequest csr; 953 954 csr.h_cs = &keys[i].h_cs; 955 csr.blinded_planchet 956 = &pd.blinded_planchet.blinded_message->details.cs_blinded_message; 957 ec = TALER_CRYPTO_helper_cs_batch_sign ( 958 dh, 959 1, 960 &csr, 961 true, 962 &ds); 963 if (TALER_EC_NONE != ec) 964 break; 965 delay = GNUNET_TIME_absolute_get_duration (start); 966 duration = GNUNET_TIME_relative_add (duration, 967 delay); 968 TALER_blinded_denom_sig_free (&ds); 969 j++; 970 if (NUM_SIGN_PERFS <= j) 971 break; 972 } 973 TALER_blinded_planchet_free (&pd.blinded_planchet); 974 } 975 } /* for i */ 976 } /* for j */ 977 fprintf (stderr, 978 "%u (%s) signature operations took %s\n", 979 (unsigned int) NUM_SIGN_PERFS, 980 type, 981 GNUNET_STRINGS_relative_time_to_string (duration, 982 GNUNET_YES)); 983 return 0; 984 } 985 986 987 /** 988 * Parallel signing logic. 989 * 990 * @param esh handle to the helper 991 * @return 0 on success 992 */ 993 static int 994 par_signing (struct GNUNET_CONFIGURATION_Handle *cfg) 995 { 996 struct GNUNET_TIME_Absolute start; 997 struct GNUNET_TIME_Relative duration; 998 pid_t pids[NUM_CORES]; 999 struct TALER_CRYPTO_CsDenominationHelper *dh; 1000 1001 start = GNUNET_TIME_absolute_get (); 1002 for (unsigned int i = 0; i<NUM_CORES; i++) 1003 { 1004 pids[i] = fork (); 1005 num_keys = 0; 1006 GNUNET_assert (-1 != pids[i]); 1007 if (0 == pids[i]) 1008 { 1009 int ret; 1010 1011 dh = TALER_CRYPTO_helper_cs_connect (cfg, 1012 "taler-exchange", 1013 &key_cb, 1014 NULL); 1015 GNUNET_assert (NULL != dh); 1016 ret = perf_signing (dh, 1017 "parallel"); 1018 TALER_CRYPTO_helper_cs_disconnect (dh); 1019 free_keys (); 1020 exit (ret); 1021 } 1022 } 1023 for (unsigned int i = 0; i<NUM_CORES; i++) 1024 { 1025 int wstatus; 1026 1027 GNUNET_assert (pids[i] == 1028 waitpid (pids[i], 1029 &wstatus, 1030 0)); 1031 } 1032 duration = GNUNET_TIME_absolute_get_duration (start); 1033 fprintf (stderr, 1034 "%u (parallel) signature operations took %s (total real time)\n", 1035 (unsigned int) NUM_SIGN_PERFS * NUM_CORES, 1036 GNUNET_STRINGS_relative_time_to_string (duration, 1037 GNUNET_YES)); 1038 return 0; 1039 } 1040 1041 1042 /** 1043 * Main entry point into the test logic with the helper already running. 1044 */ 1045 static int 1046 run_test (void) 1047 { 1048 struct GNUNET_CONFIGURATION_Handle *cfg; 1049 struct TALER_CRYPTO_CsDenominationHelper *dh; 1050 struct timespec req = { 1051 .tv_nsec = 250000000 1052 }; 1053 int ret; 1054 1055 cfg = GNUNET_CONFIGURATION_create (TALER_EXCHANGE_project_data ()); 1056 if (GNUNET_OK != 1057 GNUNET_CONFIGURATION_load (cfg, 1058 "test_helper_cs.conf")) 1059 { 1060 GNUNET_break (0); 1061 return 77; 1062 } 1063 1064 fprintf (stderr, "Waiting for helper to start ... "); 1065 for (unsigned int i = 0; i<100; i++) 1066 { 1067 nanosleep (&req, 1068 NULL); 1069 dh = TALER_CRYPTO_helper_cs_connect (cfg, 1070 "taler-exchange", 1071 &key_cb, 1072 NULL); 1073 if (NULL != dh) 1074 break; 1075 fprintf (stderr, "."); 1076 } 1077 if (NULL == dh) 1078 { 1079 fprintf (stderr, 1080 "\nFAILED: timeout trying to connect to helper\n"); 1081 GNUNET_CONFIGURATION_destroy (cfg); 1082 return 1; 1083 } 1084 if (0 == num_keys) 1085 { 1086 fprintf (stderr, 1087 "\nFAILED: timeout trying to connect to helper\n"); 1088 TALER_CRYPTO_helper_cs_disconnect (dh); 1089 GNUNET_CONFIGURATION_destroy (cfg); 1090 return 1; 1091 } 1092 fprintf (stderr, 1093 " Done (%u keys)\n", 1094 num_keys); 1095 ret = 0; 1096 if (0 == ret) 1097 ret = test_revocation (dh); 1098 if (0 == ret) 1099 ret = test_r_derive (dh); 1100 if (0 == ret) 1101 ret = test_signing (dh); 1102 if (0 == ret) 1103 ret = test_batch_signing (dh, 1104 2, 1105 true); 1106 if (0 == ret) 1107 ret = test_batch_signing (dh, 1108 64, 1109 true); 1110 for (unsigned int i = 0; i<4; i++) 1111 { 1112 static unsigned int batches[] = { 1, 4, 16, 64 }; 1113 unsigned int batch_size = batches[i]; 1114 struct GNUNET_TIME_Absolute start; 1115 struct GNUNET_TIME_Relative duration; 1116 1117 start = GNUNET_TIME_absolute_get (); 1118 if (0 != ret) 1119 break; 1120 ret = test_batch_signing (dh, 1121 batch_size, 1122 false); 1123 duration = GNUNET_TIME_absolute_get_duration (start); 1124 fprintf (stderr, 1125 "%4u (batch) signature operations took %s (total real time)\n", 1126 (unsigned int) batch_size, 1127 GNUNET_STRINGS_relative_time_to_string (duration, 1128 GNUNET_YES)); 1129 } 1130 if (0 == ret) 1131 ret = perf_signing (dh, 1132 "sequential"); 1133 TALER_CRYPTO_helper_cs_disconnect (dh); 1134 free_keys (); 1135 if (0 == ret) 1136 ret = par_signing (cfg); 1137 /* clean up our state */ 1138 GNUNET_CONFIGURATION_destroy (cfg); 1139 return ret; 1140 } 1141 1142 1143 int 1144 main (int argc, 1145 const char *const argv[]) 1146 { 1147 struct GNUNET_Process *helper; 1148 char *libexec_dir; 1149 char *binary_name; 1150 int ret; 1151 enum GNUNET_OS_ProcessStatusType type; 1152 unsigned long code; 1153 const char *loglev = "WARNING"; 1154 1155 (void) argc; 1156 (void) argv; 1157 unsetenv ("XDG_DATA_HOME"); 1158 unsetenv ("XDG_CONFIG_HOME"); 1159 GNUNET_log_setup ("test-helper-cs", 1160 loglev, 1161 NULL); 1162 libexec_dir = GNUNET_OS_installation_get_path (TALER_EXCHANGE_project_data (), 1163 GNUNET_OS_IPK_BINDIR); 1164 GNUNET_asprintf (&binary_name, 1165 "%s/%s", 1166 libexec_dir, 1167 "taler-exchange-secmod-cs"); 1168 GNUNET_free (libexec_dir); 1169 helper = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR); 1170 if (GNUNET_OK != 1171 GNUNET_process_run_command_va (helper, 1172 binary_name, 1173 binary_name, 1174 "-c", 1175 "test_helper_cs.conf", 1176 "-L", 1177 loglev, 1178 NULL)) 1179 { 1180 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 1181 "exec", 1182 binary_name); 1183 GNUNET_process_destroy (helper); 1184 GNUNET_free (binary_name); 1185 return 77; 1186 } 1187 GNUNET_free (binary_name); 1188 ret = run_test (); 1189 1190 GNUNET_break (GNUNET_OK == 1191 GNUNET_process_kill (helper, 1192 SIGTERM)); 1193 if (GNUNET_OK != 1194 GNUNET_process_wait (helper, 1195 true, 1196 &type, 1197 &code)) 1198 { 1199 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1200 "Helper process did not die voluntarily, killing hard\n"); 1201 GNUNET_break (GNUNET_OK == 1202 GNUNET_process_kill (helper, 1203 SIGKILL)); 1204 ret = 4; 1205 } 1206 else if ( (GNUNET_OS_PROCESS_EXITED != type) || 1207 (0 != code) ) 1208 { 1209 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1210 "Helper died with unexpected status %d/%d\n", 1211 (int) type, 1212 (int) code); 1213 ret = 5; 1214 } 1215 GNUNET_process_destroy (helper); 1216 return ret; 1217 } 1218 1219 1220 /* end of test_helper_cs.c */