secmod_eddsa.c (32008B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-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/secmod_eddsa.c 18 * @brief Standalone process to perform private key EDDSA operations 19 * @author Christian Grothoff 20 * 21 * Key design points: 22 * - EVERY thread of the exchange will have its own pair of connections to the 23 * crypto helpers. This way, every threat will also have its own /keys state 24 * and avoid the need to synchronize on those. 25 * - auditor signatures and master signatures are to be kept in the exchange DB, 26 * and merged with the public keys of the helper by the exchange HTTPD! 27 * - the main loop of the helper is SINGLE-THREADED, but there are 28 * threads for crypto-workers which (only) do the signing in parallel, 29 * one per client. 30 * - thread-safety: signing happens in parallel, thus when REMOVING private keys, 31 * we must ensure that all signers are done before we fully free() the 32 * private key. This is done by reference counting (as work is always 33 * assigned and collected by the main thread). 34 */ 35 #include "taler/platform.h" 36 #include "taler/taler_util.h" 37 #include "secmod_eddsa.h" 38 #include <gcrypt.h> 39 #include <pthread.h> 40 #include "taler/taler_error_codes.h" 41 #include "taler/taler_signatures.h" 42 #include "secmod_common.h" 43 #include <poll.h> 44 45 46 /** 47 * One particular key. 48 */ 49 struct Key 50 { 51 52 /** 53 * Kept in a DLL. Sorted by anchor time. 54 */ 55 struct Key *next; 56 57 /** 58 * Kept in a DLL. Sorted by anchor time. 59 */ 60 struct Key *prev; 61 62 /** 63 * Name of the file this key is stored under. 64 */ 65 char *filename; 66 67 /** 68 * The private key. 69 */ 70 struct TALER_ExchangePrivateKeyP exchange_priv; 71 72 /** 73 * The public key. 74 */ 75 struct TALER_ExchangePublicKeyP exchange_pub; 76 77 /** 78 * Time at which this key is supposed to become valid. 79 */ 80 struct GNUNET_TIME_Timestamp anchor; 81 82 /** 83 * Generation when this key was created or revoked. 84 */ 85 uint64_t key_gen; 86 87 /** 88 * Reference counter. Counts the number of threads that are 89 * using this key at this time. 90 */ 91 unsigned int rc; 92 93 /** 94 * Flag set to true if this key has been purged and the memory 95 * must be freed as soon as @e rc hits zero. 96 */ 97 bool purge; 98 99 }; 100 101 102 /** 103 * Head of DLL of actual keys, sorted by anchor. 104 */ 105 static struct Key *keys_head; 106 107 /** 108 * Tail of DLL of actual keys. 109 */ 110 static struct Key *keys_tail; 111 112 /** 113 * How long can a key be used? 114 */ 115 static struct GNUNET_TIME_Relative duration; 116 117 /** 118 * Command-line options for various TALER_SECMOD_XXX_run() functions. 119 */ 120 static struct TALER_SECMOD_Options *globals; 121 122 /** 123 * Where do we store the keys? 124 */ 125 static char *keydir; 126 127 /** 128 * How much should coin creation duration overlap 129 * with the next key? Basically, the starting time of two 130 * keys is always #duration - #overlap_duration apart. 131 */ 132 static struct GNUNET_TIME_Relative overlap_duration; 133 134 /** 135 * How long into the future do we pre-generate keys? 136 */ 137 static struct GNUNET_TIME_Relative lookahead_sign; 138 139 /** 140 * Task run to generate new keys. 141 */ 142 static struct GNUNET_SCHEDULER_Task *keygen_task; 143 144 /** 145 * Lock for the keys queue. 146 */ 147 static pthread_mutex_t keys_lock; 148 149 /** 150 * Current key generation. 151 */ 152 static uint64_t key_gen; 153 154 155 /** 156 * Notify @a client about @a key becoming available. 157 * 158 * @param[in,out] client the client to notify; possible freed if transmission fails 159 * @param key the key to notify @a client about 160 * @return #GNUNET_OK on success 161 */ 162 static enum GNUNET_GenericReturnValue 163 notify_client_key_add (struct TES_Client *client, 164 const struct Key *key) 165 { 166 struct TALER_CRYPTO_EddsaKeyAvailableNotification an = { 167 .header.size = htons (sizeof (an)), 168 .header.type = htons (TALER_HELPER_EDDSA_MT_AVAIL), 169 .anchor_time = GNUNET_TIME_timestamp_hton (key->anchor), 170 .duration = GNUNET_TIME_relative_hton (duration), 171 .exchange_pub = key->exchange_pub, 172 .secm_pub = TES_smpub 173 }; 174 175 TALER_exchange_secmod_eddsa_sign (&key->exchange_pub, 176 key->anchor, 177 duration, 178 &TES_smpriv, 179 &an.secm_sig); 180 if (GNUNET_OK != 181 TES_transmit (client->csock, 182 &an.header)) 183 { 184 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 185 "Client %p must have disconnected\n", 186 client); 187 return GNUNET_SYSERR; 188 } 189 return GNUNET_OK; 190 } 191 192 193 /** 194 * Notify @a client about @a key being purged. 195 * 196 * @param[in,out] client the client to notify; possible freed if transmission fails 197 * @param key the key to notify @a client about 198 * @return #GNUNET_OK on success 199 */ 200 static enum GNUNET_GenericReturnValue 201 notify_client_key_del (struct TES_Client *client, 202 const struct Key *key) 203 { 204 struct TALER_CRYPTO_EddsaKeyPurgeNotification pn = { 205 .header.type = htons (TALER_HELPER_EDDSA_MT_PURGE), 206 .header.size = htons (sizeof (pn)), 207 .exchange_pub = key->exchange_pub 208 }; 209 210 if (GNUNET_OK != 211 TES_transmit (client->csock, 212 &pn.header)) 213 { 214 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 215 "Client %p must have disconnected\n", 216 client); 217 return GNUNET_SYSERR; 218 } 219 return GNUNET_OK; 220 } 221 222 223 /** 224 * Handle @a client request @a sr to create signature. Create the 225 * signature using the respective key and return the result to 226 * the client. 227 * 228 * @param client the client making the request 229 * @param sr the request details 230 * @return #GNUNET_OK on success 231 */ 232 static enum GNUNET_GenericReturnValue 233 handle_sign_request (struct TES_Client *client, 234 const struct TALER_CRYPTO_EddsaSignRequest *sr) 235 { 236 const struct GNUNET_CRYPTO_SignaturePurpose *purpose = &sr->purpose; 237 size_t purpose_size = ntohs (sr->header.size) - sizeof (*sr) 238 + sizeof (*purpose); 239 struct Key *key; 240 struct TALER_CRYPTO_EddsaSignResponse sres = { 241 .header.size = htons (sizeof (sres)), 242 .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGNATURE) 243 }; 244 enum TALER_ErrorCode ec; 245 246 if (purpose_size != htonl (purpose->size)) 247 { 248 struct TALER_CRYPTO_EddsaSignFailure sf = { 249 .header.size = htons (sizeof (sr)), 250 .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE), 251 .ec = htonl (TALER_EC_GENERIC_PARAMETER_MALFORMED) 252 }; 253 254 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 255 "Signing request failed, request malformed\n"); 256 return TES_transmit (client->csock, 257 &sf.header); 258 } 259 260 GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); 261 key = keys_head; 262 while ( (NULL != key) && 263 (GNUNET_TIME_absolute_is_past ( 264 GNUNET_TIME_absolute_add (key->anchor.abs_time, 265 duration))) ) 266 { 267 struct Key *nxt = key->next; 268 269 if (0 != key->rc) 270 break; /* do later */ 271 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 272 "Deleting past key %s (expired %s ago)\n", 273 TALER_B2S (&nxt->exchange_pub), 274 GNUNET_TIME_relative2s ( 275 GNUNET_TIME_absolute_get_duration ( 276 GNUNET_TIME_absolute_add (key->anchor.abs_time, 277 duration)), 278 GNUNET_YES)); 279 GNUNET_CONTAINER_DLL_remove (keys_head, 280 keys_tail, 281 key); 282 if ( (! key->purge) && 283 (0 != unlink (key->filename)) ) 284 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 285 "unlink", 286 key->filename); 287 GNUNET_free (key->filename); 288 GNUNET_free (key); 289 key = nxt; 290 } 291 if (NULL == key) 292 { 293 GNUNET_break (0); 294 ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; 295 } 296 else 297 { 298 GNUNET_assert (key->rc < UINT_MAX); 299 key->rc++; 300 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 301 302 if (GNUNET_OK != 303 GNUNET_CRYPTO_eddsa_sign_ (&key->exchange_priv.eddsa_priv, 304 purpose, 305 &sres.exchange_sig.eddsa_signature)) 306 ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 307 else 308 ec = TALER_EC_NONE; 309 sres.exchange_pub = key->exchange_pub; 310 GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); 311 GNUNET_assert (key->rc > 0); 312 key->rc--; 313 } 314 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 315 if (TALER_EC_NONE != ec) 316 { 317 struct TALER_CRYPTO_EddsaSignFailure sf = { 318 .header.size = htons (sizeof (sf)), 319 .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE), 320 .ec = htonl ((uint32_t) ec) 321 }; 322 323 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 324 "Signing request %p failed, worker failed to produce signature\n", 325 client); 326 return TES_transmit (client->csock, 327 &sf.header); 328 } 329 return TES_transmit (client->csock, 330 &sres.header); 331 } 332 333 334 /** 335 * Initialize key material for key @a key (also on disk). 336 * 337 * @param[in,out] key to compute key material for 338 * @param position where in the DLL will the @a key go 339 * @return #GNUNET_OK on success 340 */ 341 static enum GNUNET_GenericReturnValue 342 setup_key (struct Key *key, 343 struct Key *position) 344 { 345 struct GNUNET_CRYPTO_EddsaPrivateKey priv; 346 struct GNUNET_CRYPTO_EddsaPublicKey pub; 347 348 GNUNET_CRYPTO_eddsa_key_create (&priv); 349 GNUNET_CRYPTO_eddsa_key_get_public (&priv, 350 &pub); 351 GNUNET_asprintf (&key->filename, 352 "%s/%llu", 353 keydir, 354 (unsigned long long) (key->anchor.abs_time.abs_value_us 355 / GNUNET_TIME_UNIT_SECONDS.rel_value_us 356 )); 357 if (GNUNET_OK != 358 GNUNET_DISK_fn_write (key->filename, 359 &priv, 360 sizeof (priv), 361 GNUNET_DISK_PERM_USER_READ)) 362 { 363 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 364 "write", 365 key->filename); 366 return GNUNET_SYSERR; 367 } 368 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 369 "Setup fresh private key in `%s'\n", 370 key->filename); 371 key->key_gen = key_gen; 372 key->exchange_priv.eddsa_priv = priv; 373 key->exchange_pub.eddsa_pub = pub; 374 GNUNET_CONTAINER_DLL_insert_after (keys_head, 375 keys_tail, 376 position, 377 key); 378 return GNUNET_OK; 379 } 380 381 382 /** 383 * The validity period of a key @a key has expired. Purge it. 384 * 385 * @param[in] key expired or revoked key to purge 386 */ 387 static void 388 purge_key (struct Key *key) 389 { 390 if (key->purge) 391 { 392 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 393 "Key %s already purged, skipping\n", 394 TALER_B2S (&key->exchange_pub)); 395 return; 396 } 397 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 398 "Purging key %s\n", 399 TALER_B2S (&key->exchange_pub)); 400 if (0 != unlink (key->filename)) 401 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 402 "unlink", 403 key->filename); 404 key->purge = true; 405 key->key_gen = key_gen; 406 GNUNET_free (key->filename); 407 } 408 409 410 /** 411 * A @a client informs us that a key has been revoked. 412 * Check if the key is still in use, and if so replace (!) 413 * it with a fresh key. 414 * 415 * @param client the client making the request 416 * @param rr the revocation request 417 * @return #GNUNET_OK on success 418 */ 419 static enum GNUNET_GenericReturnValue 420 handle_revoke_request (struct TES_Client *client, 421 const struct TALER_CRYPTO_EddsaRevokeRequest *rr) 422 { 423 struct Key *key; 424 struct Key *nkey; 425 426 (void) client; 427 key = NULL; 428 GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); 429 for (struct Key *pos = keys_head; 430 NULL != pos; 431 pos = pos->next) 432 if (0 == GNUNET_memcmp (&pos->exchange_pub, 433 &rr->exchange_pub)) 434 { 435 key = pos; 436 break; 437 } 438 if (NULL == key) 439 { 440 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 441 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 442 "Revocation request ignored, key unknown\n"); 443 return GNUNET_OK; 444 } 445 if (key->purge) 446 { 447 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 448 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 449 "Revocation request ignored, key %s already revoked\n", 450 TALER_B2S (&key->exchange_pub)); 451 return GNUNET_OK; 452 } 453 key_gen++; 454 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 455 "Revoking key %s, bumping generation to %llu\n", 456 TALER_B2S (&key->exchange_pub), 457 (unsigned long long) key_gen); 458 purge_key (key); 459 460 /* Setup replacement key */ 461 nkey = GNUNET_new (struct Key); 462 nkey->anchor = key->anchor; 463 if (GNUNET_OK != 464 setup_key (nkey, 465 key)) 466 { 467 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 468 GNUNET_break (0); 469 GNUNET_SCHEDULER_shutdown (); 470 globals->global_ret = EXIT_FAILURE; 471 return GNUNET_SYSERR; 472 } 473 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 474 TES_wake_clients (); 475 return GNUNET_OK; 476 } 477 478 479 /** 480 * Handle @a hdr message received from @a client. 481 * 482 * @param client the client that received the message 483 * @param hdr message that was received 484 * @return #GNUNET_OK on success 485 */ 486 static enum GNUNET_GenericReturnValue 487 eddsa_work_dispatch (struct TES_Client *client, 488 const struct GNUNET_MessageHeader *hdr) 489 { 490 uint16_t msize = ntohs (hdr->size); 491 492 switch (ntohs (hdr->type)) 493 { 494 case TALER_HELPER_EDDSA_MT_REQ_SIGN: 495 if (msize < sizeof (struct TALER_CRYPTO_EddsaSignRequest)) 496 { 497 GNUNET_break_op (0); 498 return GNUNET_SYSERR; 499 } 500 return handle_sign_request ( 501 client, 502 (const struct TALER_CRYPTO_EddsaSignRequest *) hdr); 503 case TALER_HELPER_EDDSA_MT_REQ_REVOKE: 504 if (msize != sizeof (struct TALER_CRYPTO_EddsaRevokeRequest)) 505 { 506 GNUNET_break_op (0); 507 return GNUNET_SYSERR; 508 } 509 return handle_revoke_request ( 510 client, 511 (const struct TALER_CRYPTO_EddsaRevokeRequest *) hdr); 512 default: 513 GNUNET_break_op (0); 514 return GNUNET_SYSERR; 515 } 516 } 517 518 519 /** 520 * Send our initial key set to @a client together with the 521 * "sync" terminator. 522 * 523 * @param client the client to inform 524 * @return #GNUNET_OK on success 525 */ 526 static enum GNUNET_GenericReturnValue 527 eddsa_client_init (struct TES_Client *client) 528 { 529 GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); 530 for (struct Key *key = keys_head; 531 NULL != key; 532 key = key->next) 533 { 534 if (GNUNET_OK != 535 notify_client_key_add (client, 536 key)) 537 { 538 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 539 GNUNET_break (0); 540 return GNUNET_SYSERR; 541 } 542 } 543 client->key_gen = key_gen; 544 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 545 { 546 struct GNUNET_MessageHeader synced = { 547 .type = htons (TALER_HELPER_EDDSA_SYNCED), 548 .size = htons (sizeof (synced)) 549 }; 550 551 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 552 "Client %p synced\n", 553 client); 554 if (GNUNET_OK != 555 TES_transmit (client->csock, 556 &synced)) 557 { 558 GNUNET_break (0); 559 return GNUNET_SYSERR; 560 } 561 } 562 return GNUNET_OK; 563 } 564 565 566 /** 567 * Notify @a client about all changes to the keys since 568 * the last generation known to the @a client. 569 * 570 * @param client the client to notify 571 * @return #GNUNET_OK on success 572 */ 573 static enum GNUNET_GenericReturnValue 574 eddsa_update_client_keys (struct TES_Client *client) 575 { 576 GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); 577 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 578 "Updating client %p to generation %llu\n", 579 client, 580 (unsigned long long) key_gen); 581 for (struct Key *key = keys_head; 582 NULL != key; 583 key = key->next) 584 { 585 if (key->key_gen <= client->key_gen) 586 { 587 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 588 "Skipping key %s, no change since generation %llu\n", 589 TALER_B2S (&key->exchange_pub), 590 (unsigned long long) client->key_gen); 591 continue; 592 } 593 if (key->purge) 594 { 595 if (GNUNET_OK != 596 notify_client_key_del (client, 597 key)) 598 { 599 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 600 return GNUNET_SYSERR; 601 } 602 } 603 else 604 { 605 if (GNUNET_OK != 606 notify_client_key_add (client, 607 key)) 608 { 609 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 610 return GNUNET_SYSERR; 611 } 612 } 613 } 614 client->key_gen = key_gen; 615 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 616 return GNUNET_OK; 617 } 618 619 620 /** 621 * Create a new key (we do not have enough). 622 * 623 * @return #GNUNET_OK on success 624 */ 625 static enum GNUNET_GenericReturnValue 626 create_key (void) 627 { 628 struct Key *key; 629 struct GNUNET_TIME_Timestamp anchor; 630 631 anchor = GNUNET_TIME_timestamp_get (); 632 if (NULL != keys_tail) 633 { 634 struct GNUNET_TIME_Absolute abs; 635 636 abs = GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time, 637 GNUNET_TIME_relative_subtract ( 638 duration, 639 overlap_duration)); 640 if (GNUNET_TIME_absolute_cmp (anchor.abs_time, 641 <, 642 abs)) 643 anchor = GNUNET_TIME_absolute_to_timestamp (abs); 644 } 645 key = GNUNET_new (struct Key); 646 key->anchor = anchor; 647 if (GNUNET_OK != 648 setup_key (key, 649 keys_tail)) 650 { 651 GNUNET_break (0); 652 GNUNET_free (key); 653 GNUNET_SCHEDULER_shutdown (); 654 globals->global_ret = EXIT_FAILURE; 655 return GNUNET_SYSERR; 656 } 657 return GNUNET_OK; 658 } 659 660 661 /** 662 * At what time does the current key set require its next action? Basically, 663 * the minimum of the expiration time of the oldest key, and the expiration 664 * time of the newest key minus the #lookahead_sign time. 665 */ 666 static struct GNUNET_TIME_Absolute 667 key_action_time (void) 668 { 669 struct Key *nxt; 670 671 nxt = keys_head; 672 while ( (NULL != nxt) && 673 (nxt->purge) ) 674 nxt = nxt->next; 675 if (NULL == nxt) 676 return GNUNET_TIME_UNIT_ZERO_ABS; 677 return GNUNET_TIME_absolute_min ( 678 GNUNET_TIME_absolute_add (nxt->anchor.abs_time, 679 duration), 680 GNUNET_TIME_absolute_subtract ( 681 GNUNET_TIME_absolute_subtract ( 682 GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time, 683 duration), 684 lookahead_sign), 685 overlap_duration)); 686 } 687 688 689 /** 690 * Create new keys and expire ancient keys. 691 * 692 * @param cls NULL 693 */ 694 static void 695 update_keys (void *cls) 696 { 697 bool wake = false; 698 struct Key *nxt; 699 700 (void) cls; 701 keygen_task = NULL; 702 GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); 703 /* create new keys */ 704 while ( (NULL == keys_tail) || 705 GNUNET_TIME_absolute_is_past ( 706 GNUNET_TIME_absolute_subtract ( 707 GNUNET_TIME_absolute_subtract ( 708 GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time, 709 duration), 710 lookahead_sign), 711 overlap_duration)) ) 712 { 713 if (! wake) 714 { 715 key_gen++; 716 wake = true; 717 } 718 if (GNUNET_OK != 719 create_key ()) 720 { 721 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 722 GNUNET_break (0); 723 globals->global_ret = EXIT_FAILURE; 724 GNUNET_SCHEDULER_shutdown (); 725 return; 726 } 727 } 728 nxt = keys_head; 729 /* purge expired keys */ 730 while ( (NULL != nxt) && 731 GNUNET_TIME_absolute_is_past ( 732 GNUNET_TIME_absolute_add (nxt->anchor.abs_time, 733 duration))) 734 { 735 if (! wake) 736 { 737 key_gen++; 738 wake = true; 739 } 740 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 741 "Purging past key %s (expired %s ago)\n", 742 TALER_B2S (&nxt->exchange_pub), 743 GNUNET_TIME_relative2s ( 744 GNUNET_TIME_absolute_get_duration ( 745 GNUNET_TIME_absolute_add (nxt->anchor.abs_time, 746 duration)), 747 GNUNET_YES)); 748 purge_key (nxt); 749 nxt = nxt->next; 750 } 751 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 752 if (wake) 753 TES_wake_clients (); 754 keygen_task = GNUNET_SCHEDULER_add_at (key_action_time (), 755 &update_keys, 756 NULL); 757 } 758 759 760 /** 761 * Parse private key from @a filename in @a buf. 762 * 763 * @param filename name of the file we are parsing, for logging 764 * @param buf key material 765 * @param buf_size number of bytes in @a buf 766 * @return #GNUNET_OK on success 767 */ 768 static enum GNUNET_GenericReturnValue 769 parse_key (const char *filename, 770 const void *buf, 771 size_t buf_size) 772 { 773 struct GNUNET_CRYPTO_EddsaPrivateKey priv; 774 char *anchor_s; 775 char dummy; 776 unsigned long long anchor_ll; 777 struct GNUNET_TIME_Timestamp anchor; 778 779 anchor_s = strrchr (filename, 780 '/'); 781 if (NULL == anchor_s) 782 { 783 /* File in a directory without '/' in the name, this makes no sense. */ 784 GNUNET_break (0); 785 return GNUNET_SYSERR; 786 } 787 anchor_s++; 788 if (1 != sscanf (anchor_s, 789 "%llu%c", 790 &anchor_ll, 791 &dummy)) 792 { 793 /* Filenames in KEYDIR must ONLY be the anchor time in seconds! */ 794 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 795 "Filename `%s' invalid for key file, skipping\n", 796 filename); 797 return GNUNET_SYSERR; 798 } 799 anchor.abs_time.abs_value_us = anchor_ll 800 * GNUNET_TIME_UNIT_SECONDS.rel_value_us; 801 if (anchor_ll != anchor.abs_time.abs_value_us 802 / GNUNET_TIME_UNIT_SECONDS.rel_value_us) 803 { 804 /* Integer overflow. Bad, invalid filename. */ 805 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 806 "Filename `%s' invalid for key file, skipping\n", 807 filename); 808 return GNUNET_SYSERR; 809 } 810 if (buf_size != sizeof (priv)) 811 { 812 /* Parser failure. */ 813 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 814 "File `%s' is malformed, skipping\n", 815 filename); 816 return GNUNET_SYSERR; 817 } 818 GNUNET_memcpy (&priv, 819 buf, 820 buf_size); 821 822 { 823 struct GNUNET_CRYPTO_EddsaPublicKey pub; 824 struct Key *key; 825 struct Key *before; 826 827 GNUNET_CRYPTO_eddsa_key_get_public (&priv, 828 &pub); 829 GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); 830 key = GNUNET_new (struct Key); 831 key->exchange_priv.eddsa_priv = priv; 832 key->exchange_pub.eddsa_pub = pub; 833 key->anchor = anchor; 834 key->filename = GNUNET_strdup (filename); 835 key->key_gen = key_gen; 836 before = NULL; 837 for (struct Key *pos = keys_head; 838 NULL != pos; 839 pos = pos->next) 840 { 841 if (GNUNET_TIME_timestamp_cmp (pos->anchor, >, anchor)) 842 break; 843 before = pos; 844 } 845 GNUNET_CONTAINER_DLL_insert_after (keys_head, 846 keys_tail, 847 before, 848 key); 849 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 850 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 851 "Imported key from `%s'\n", 852 filename); 853 } 854 return GNUNET_OK; 855 } 856 857 858 /** 859 * Import a private key from @a filename. 860 * 861 * @param cls NULL 862 * @param filename name of a file in the directory 863 */ 864 static enum GNUNET_GenericReturnValue 865 import_key (void *cls, 866 const char *filename) 867 { 868 struct GNUNET_DISK_FileHandle *fh; 869 struct GNUNET_DISK_MapHandle *map; 870 void *ptr; 871 int fd; 872 struct stat sbuf; 873 874 (void) cls; 875 { 876 struct stat lsbuf; 877 878 if (0 != lstat (filename, 879 &lsbuf)) 880 { 881 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 882 "lstat", 883 filename); 884 return GNUNET_OK; 885 } 886 if (! S_ISREG (lsbuf.st_mode)) 887 { 888 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 889 "File `%s' is not a regular file, which is not allowed for private keys!\n", 890 filename); 891 return GNUNET_OK; 892 } 893 } 894 895 fd = open (filename, 896 O_RDONLY | O_CLOEXEC); 897 if (-1 == fd) 898 { 899 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 900 "open", 901 filename); 902 return GNUNET_OK; 903 } 904 if (0 != fstat (fd, 905 &sbuf)) 906 { 907 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 908 "stat", 909 filename); 910 GNUNET_break (0 == close (fd)); 911 return GNUNET_OK; 912 } 913 if (! S_ISREG (sbuf.st_mode)) 914 { 915 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 916 "File `%s' is not a regular file, which is not allowed for private keys!\n", 917 filename); 918 GNUNET_break (0 == close (fd)); 919 return GNUNET_OK; 920 } 921 if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO))) 922 { 923 /* permission are NOT tight, try to patch them up! */ 924 if (0 != 925 fchmod (fd, 926 S_IRUSR)) 927 { 928 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 929 "fchmod", 930 filename); 931 /* refuse to use key if file has wrong permissions */ 932 GNUNET_break (0 == close (fd)); 933 return GNUNET_OK; 934 } 935 } 936 fh = GNUNET_DISK_get_handle_from_int_fd (fd); 937 if (NULL == fh) 938 { 939 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 940 "open", 941 filename); 942 GNUNET_break (0 == close (fd)); 943 return GNUNET_OK; 944 } 945 if (sbuf.st_size > 2048) 946 { 947 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 948 "File `%s' to big to be a private key\n", 949 filename); 950 GNUNET_DISK_file_close (fh); 951 return GNUNET_OK; 952 } 953 ptr = GNUNET_DISK_file_map (fh, 954 &map, 955 GNUNET_DISK_MAP_TYPE_READ, 956 (size_t) sbuf.st_size); 957 if (NULL == ptr) 958 { 959 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 960 "mmap", 961 filename); 962 GNUNET_DISK_file_close (fh); 963 return GNUNET_OK; 964 } 965 (void) parse_key (filename, 966 ptr, 967 (size_t) sbuf.st_size); 968 GNUNET_DISK_file_unmap (map); 969 GNUNET_DISK_file_close (fh); 970 return GNUNET_OK; 971 } 972 973 974 /** 975 * Load the various duration values from @a kcfg. 976 * 977 * @param cfg configuration to use 978 * @return #GNUNET_OK on success 979 */ 980 static enum GNUNET_GenericReturnValue 981 load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg) 982 { 983 char *secname; 984 985 GNUNET_asprintf (&secname, 986 "%s-secmod-eddsa", 987 globals->section); 988 if (GNUNET_OK != 989 GNUNET_CONFIGURATION_get_value_time (cfg, 990 secname, 991 "OVERLAP_DURATION", 992 &overlap_duration)) 993 { 994 GNUNET_free (secname); 995 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 996 secname, 997 "OVERLAP_DURATION"); 998 return GNUNET_SYSERR; 999 } 1000 if (GNUNET_OK != 1001 GNUNET_CONFIGURATION_get_value_time (cfg, 1002 secname, 1003 "DURATION", 1004 &duration)) 1005 { 1006 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1007 secname, 1008 "DURATION"); 1009 GNUNET_free (secname); 1010 return GNUNET_SYSERR; 1011 } 1012 if (GNUNET_OK != 1013 GNUNET_CONFIGURATION_get_value_time (cfg, 1014 secname, 1015 "LOOKAHEAD_SIGN", 1016 &lookahead_sign)) 1017 { 1018 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1019 secname, 1020 "LOOKAHEAD_SIGN"); 1021 GNUNET_free (secname); 1022 return GNUNET_SYSERR; 1023 } 1024 GNUNET_free (secname); 1025 return GNUNET_OK; 1026 } 1027 1028 1029 /** 1030 * Function run on shutdown. Stops the various jobs (nicely). 1031 * 1032 * @param cls NULL 1033 */ 1034 static void 1035 do_shutdown (void *cls) 1036 { 1037 (void) cls; 1038 TES_listen_stop (); 1039 if (NULL != keygen_task) 1040 { 1041 GNUNET_SCHEDULER_cancel (keygen_task); 1042 keygen_task = NULL; 1043 } 1044 } 1045 1046 1047 void 1048 TALER_SECMOD_eddsa_run (void *cls, 1049 char *const *args, 1050 const char *cfgfile, 1051 const struct GNUNET_CONFIGURATION_Handle *cfg) 1052 { 1053 static struct TES_Callbacks cb = { 1054 .dispatch = eddsa_work_dispatch, 1055 .updater = eddsa_update_client_keys, 1056 .init = eddsa_client_init 1057 }; 1058 struct TALER_SECMOD_Options *opt = cls; 1059 char *secname; 1060 1061 (void) args; 1062 (void) cfgfile; 1063 globals = opt; 1064 if (GNUNET_TIME_timestamp_cmp (opt->global_now, 1065 !=, 1066 opt->global_now_tmp)) 1067 { 1068 /* The user gave "--now", use it! */ 1069 opt->global_now = opt->global_now_tmp; 1070 } 1071 else 1072 { 1073 /* get current time again, we may be timetraveling! */ 1074 opt->global_now = GNUNET_TIME_timestamp_get (); 1075 } 1076 if (GNUNET_OK != 1077 load_durations (cfg)) 1078 { 1079 opt->global_ret = EXIT_NOTCONFIGURED; 1080 return; 1081 } 1082 GNUNET_asprintf (&secname, 1083 "%s-secmod-eddsa", 1084 opt->section); 1085 if (GNUNET_OK != 1086 GNUNET_CONFIGURATION_get_value_filename (cfg, 1087 secname, 1088 "KEY_DIR", 1089 &keydir)) 1090 { 1091 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1092 secname, 1093 "KEY_DIR"); 1094 GNUNET_free (secname); 1095 opt->global_ret = EXIT_NOTCONFIGURED; 1096 return; 1097 } 1098 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 1099 NULL); 1100 opt->global_ret = TES_listen_start (cfg, 1101 secname, 1102 &cb); 1103 GNUNET_free (secname); 1104 if (0 != opt->global_ret) 1105 return; 1106 /* Load keys */ 1107 GNUNET_break (GNUNET_OK == 1108 GNUNET_DISK_directory_create (keydir)); 1109 GNUNET_DISK_directory_scan (keydir, 1110 &import_key, 1111 NULL); 1112 if ( (NULL != keys_head) && 1113 (GNUNET_TIME_absolute_is_future (keys_head->anchor.abs_time)) ) 1114 { 1115 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1116 "Existing anchor is in %s the future. Refusing to start\n", 1117 GNUNET_TIME_relative2s ( 1118 GNUNET_TIME_absolute_get_remaining ( 1119 keys_head->anchor.abs_time), 1120 true)); 1121 opt->global_ret = EXIT_FAILURE; 1122 GNUNET_SCHEDULER_shutdown (); 1123 return; 1124 } 1125 /* start job to keep keys up-to-date; MUST be run before the #listen_task, 1126 hence with priority. */ 1127 keygen_task = GNUNET_SCHEDULER_add_with_priority ( 1128 GNUNET_SCHEDULER_PRIORITY_URGENT, 1129 &update_keys, 1130 NULL); 1131 }