secmod_eddsa.c (32333B)
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 "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 = PTHREAD_MUTEX_INITIALIZER; 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 != ntohl (purpose->size)) 247 { 248 struct TALER_CRYPTO_EddsaSignFailure sf = { 249 .header.size = htons (sizeof (sf)), 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 (&key->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 /* revoked keys must not be used for signing anymore; the replacement 292 key sits right behind the revoked one in the DLL */ 293 while ( (NULL != key) && 294 (key->purge) ) 295 key = key->next; 296 if (NULL == key) 297 { 298 GNUNET_break (0); 299 ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; 300 } 301 else 302 { 303 GNUNET_assert (key->rc < UINT_MAX); 304 key->rc++; 305 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 306 307 if (GNUNET_OK != 308 GNUNET_CRYPTO_eddsa_sign_ (&key->exchange_priv.eddsa_priv, 309 purpose, 310 &sres.exchange_sig.eddsa_signature)) 311 ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 312 else 313 ec = TALER_EC_NONE; 314 sres.exchange_pub = key->exchange_pub; 315 GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); 316 GNUNET_assert (key->rc > 0); 317 key->rc--; 318 } 319 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 320 if (TALER_EC_NONE != ec) 321 { 322 struct TALER_CRYPTO_EddsaSignFailure sf = { 323 .header.size = htons (sizeof (sf)), 324 .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE), 325 .ec = htonl ((uint32_t) ec) 326 }; 327 328 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 329 "Signing request %p failed, worker failed to produce signature\n", 330 client); 331 return TES_transmit (client->csock, 332 &sf.header); 333 } 334 return TES_transmit (client->csock, 335 &sres.header); 336 } 337 338 339 /** 340 * Initialize key material for key @a key (also on disk). 341 * 342 * @param[in,out] key to compute key material for 343 * @param position where in the DLL will the @a key go 344 * @return #GNUNET_OK on success 345 */ 346 static enum GNUNET_GenericReturnValue 347 setup_key (struct Key *key, 348 struct Key *position) 349 { 350 struct GNUNET_CRYPTO_EddsaPrivateKey priv; 351 struct GNUNET_CRYPTO_EddsaPublicKey pub; 352 353 GNUNET_CRYPTO_eddsa_key_create (&priv); 354 GNUNET_CRYPTO_eddsa_key_get_public (&priv, 355 &pub); 356 GNUNET_asprintf (&key->filename, 357 "%s/%llu", 358 keydir, 359 (unsigned long long) (key->anchor.abs_time.abs_value_us 360 / GNUNET_TIME_UNIT_SECONDS.rel_value_us 361 )); 362 if (GNUNET_OK != 363 GNUNET_DISK_fn_write (key->filename, 364 &priv, 365 sizeof (priv), 366 GNUNET_DISK_PERM_USER_READ)) 367 { 368 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 369 "write", 370 key->filename); 371 return GNUNET_SYSERR; 372 } 373 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 374 "Setup fresh private key in `%s'\n", 375 key->filename); 376 key->key_gen = key_gen; 377 key->exchange_priv.eddsa_priv = priv; 378 key->exchange_pub.eddsa_pub = pub; 379 GNUNET_CONTAINER_DLL_insert_after (keys_head, 380 keys_tail, 381 position, 382 key); 383 return GNUNET_OK; 384 } 385 386 387 /** 388 * The validity period of a key @a key has expired. Purge it. 389 * 390 * @param[in] key expired or revoked key to purge 391 */ 392 static void 393 purge_key (struct Key *key) 394 { 395 if (key->purge) 396 { 397 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 398 "Key %s already purged, skipping\n", 399 TALER_B2S (&key->exchange_pub)); 400 return; 401 } 402 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 403 "Purging key %s\n", 404 TALER_B2S (&key->exchange_pub)); 405 if (0 != unlink (key->filename)) 406 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 407 "unlink", 408 key->filename); 409 key->purge = true; 410 key->key_gen = key_gen; 411 GNUNET_free (key->filename); 412 } 413 414 415 /** 416 * A @a client informs us that a key has been revoked. 417 * Check if the key is still in use, and if so replace (!) 418 * it with a fresh key. 419 * 420 * @param client the client making the request 421 * @param rr the revocation request 422 * @return #GNUNET_OK on success 423 */ 424 static enum GNUNET_GenericReturnValue 425 handle_revoke_request (struct TES_Client *client, 426 const struct TALER_CRYPTO_EddsaRevokeRequest *rr) 427 { 428 struct Key *key; 429 struct Key *nkey; 430 431 (void) client; 432 key = NULL; 433 GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); 434 for (struct Key *pos = keys_head; 435 NULL != pos; 436 pos = pos->next) 437 if (0 == GNUNET_memcmp (&pos->exchange_pub, 438 &rr->exchange_pub)) 439 { 440 key = pos; 441 break; 442 } 443 if (NULL == key) 444 { 445 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 446 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 447 "Revocation request ignored, key unknown\n"); 448 return GNUNET_OK; 449 } 450 if (key->purge) 451 { 452 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 453 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 454 "Revocation request ignored, key %s already revoked\n", 455 TALER_B2S (&key->exchange_pub)); 456 return GNUNET_OK; 457 } 458 key_gen++; 459 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 460 "Revoking key %s, bumping generation to %llu\n", 461 TALER_B2S (&key->exchange_pub), 462 (unsigned long long) key_gen); 463 purge_key (key); 464 465 /* Setup replacement key */ 466 nkey = GNUNET_new (struct Key); 467 nkey->anchor = key->anchor; 468 if (GNUNET_OK != 469 setup_key (nkey, 470 key)) 471 { 472 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 473 GNUNET_break (0); 474 GNUNET_SCHEDULER_shutdown (); 475 globals->global_ret = EXIT_FAILURE; 476 return GNUNET_SYSERR; 477 } 478 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 479 TES_wake_clients (); 480 return GNUNET_OK; 481 } 482 483 484 /** 485 * Handle @a hdr message received from @a client. 486 * 487 * @param client the client that received the message 488 * @param hdr message that was received 489 * @return #GNUNET_OK on success 490 */ 491 static enum GNUNET_GenericReturnValue 492 eddsa_work_dispatch (struct TES_Client *client, 493 const struct GNUNET_MessageHeader *hdr) 494 { 495 uint16_t msize = ntohs (hdr->size); 496 497 switch (ntohs (hdr->type)) 498 { 499 case TALER_HELPER_EDDSA_MT_REQ_SIGN: 500 if (msize < sizeof (struct TALER_CRYPTO_EddsaSignRequest)) 501 { 502 GNUNET_break_op (0); 503 return GNUNET_SYSERR; 504 } 505 return handle_sign_request ( 506 client, 507 (const struct TALER_CRYPTO_EddsaSignRequest *) hdr); 508 case TALER_HELPER_EDDSA_MT_REQ_REVOKE: 509 if (msize != sizeof (struct TALER_CRYPTO_EddsaRevokeRequest)) 510 { 511 GNUNET_break_op (0); 512 return GNUNET_SYSERR; 513 } 514 return handle_revoke_request ( 515 client, 516 (const struct TALER_CRYPTO_EddsaRevokeRequest *) hdr); 517 default: 518 GNUNET_break_op (0); 519 return GNUNET_SYSERR; 520 } 521 } 522 523 524 /** 525 * Send our initial key set to @a client together with the 526 * "sync" terminator. 527 * 528 * @param client the client to inform 529 * @return #GNUNET_OK on success 530 */ 531 static enum GNUNET_GenericReturnValue 532 eddsa_client_init (struct TES_Client *client) 533 { 534 GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); 535 for (struct Key *key = keys_head; 536 NULL != key; 537 key = key->next) 538 { 539 if (GNUNET_OK != 540 notify_client_key_add (client, 541 key)) 542 { 543 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 544 GNUNET_break (0); 545 return GNUNET_SYSERR; 546 } 547 } 548 client->key_gen = key_gen; 549 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 550 { 551 struct GNUNET_MessageHeader synced = { 552 .type = htons (TALER_HELPER_EDDSA_SYNCED), 553 .size = htons (sizeof (synced)) 554 }; 555 556 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 557 "Client %p synced\n", 558 client); 559 if (GNUNET_OK != 560 TES_transmit (client->csock, 561 &synced)) 562 { 563 GNUNET_break (0); 564 return GNUNET_SYSERR; 565 } 566 } 567 return GNUNET_OK; 568 } 569 570 571 /** 572 * Notify @a client about all changes to the keys since 573 * the last generation known to the @a client. 574 * 575 * @param client the client to notify 576 * @return #GNUNET_OK on success 577 */ 578 static enum GNUNET_GenericReturnValue 579 eddsa_update_client_keys (struct TES_Client *client) 580 { 581 GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); 582 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 583 "Updating client %p to generation %llu\n", 584 client, 585 (unsigned long long) key_gen); 586 for (struct Key *key = keys_head; 587 NULL != key; 588 key = key->next) 589 { 590 if (key->key_gen <= client->key_gen) 591 { 592 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 593 "Skipping key %s, no change since generation %llu\n", 594 TALER_B2S (&key->exchange_pub), 595 (unsigned long long) client->key_gen); 596 continue; 597 } 598 if (key->purge) 599 { 600 if (GNUNET_OK != 601 notify_client_key_del (client, 602 key)) 603 { 604 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 605 return GNUNET_SYSERR; 606 } 607 } 608 else 609 { 610 if (GNUNET_OK != 611 notify_client_key_add (client, 612 key)) 613 { 614 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 615 return GNUNET_SYSERR; 616 } 617 } 618 } 619 client->key_gen = key_gen; 620 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 621 return GNUNET_OK; 622 } 623 624 625 /** 626 * Create a new key (we do not have enough). 627 * 628 * @return #GNUNET_OK on success 629 */ 630 static enum GNUNET_GenericReturnValue 631 create_key (void) 632 { 633 struct Key *key; 634 struct GNUNET_TIME_Timestamp anchor; 635 636 anchor = GNUNET_TIME_timestamp_get (); 637 if (NULL != keys_tail) 638 { 639 struct GNUNET_TIME_Absolute abs; 640 641 abs = GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time, 642 GNUNET_TIME_relative_subtract ( 643 duration, 644 overlap_duration)); 645 if (GNUNET_TIME_absolute_cmp (anchor.abs_time, 646 <, 647 abs)) 648 anchor = GNUNET_TIME_absolute_to_timestamp (abs); 649 } 650 key = GNUNET_new (struct Key); 651 key->anchor = anchor; 652 if (GNUNET_OK != 653 setup_key (key, 654 keys_tail)) 655 { 656 GNUNET_break (0); 657 GNUNET_free (key); 658 GNUNET_SCHEDULER_shutdown (); 659 globals->global_ret = EXIT_FAILURE; 660 return GNUNET_SYSERR; 661 } 662 return GNUNET_OK; 663 } 664 665 666 /** 667 * At what time does the current key set require its next action? Basically, 668 * the minimum of the expiration time of the oldest key, and the expiration 669 * time of the newest key minus the #lookahead_sign time. 670 * 671 * Must only be called while the #keys_lock is held. 672 */ 673 static struct GNUNET_TIME_Absolute 674 key_action_time (void) 675 { 676 struct Key *nxt; 677 678 nxt = keys_head; 679 while ( (NULL != nxt) && 680 (nxt->purge) ) 681 nxt = nxt->next; 682 if (NULL == nxt) 683 return GNUNET_TIME_UNIT_ZERO_ABS; 684 return GNUNET_TIME_absolute_min ( 685 GNUNET_TIME_absolute_add (nxt->anchor.abs_time, 686 duration), 687 GNUNET_TIME_absolute_subtract ( 688 GNUNET_TIME_absolute_subtract ( 689 GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time, 690 duration), 691 lookahead_sign), 692 overlap_duration)); 693 } 694 695 696 /** 697 * Create new keys and expire ancient keys. 698 * 699 * @param cls NULL 700 */ 701 static void 702 update_keys (void *cls) 703 { 704 bool wake = false; 705 struct Key *nxt; 706 struct GNUNET_TIME_Absolute at; 707 708 (void) cls; 709 keygen_task = NULL; 710 GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); 711 /* create new keys */ 712 while ( (NULL == keys_tail) || 713 GNUNET_TIME_absolute_is_past ( 714 GNUNET_TIME_absolute_subtract ( 715 GNUNET_TIME_absolute_subtract ( 716 GNUNET_TIME_absolute_add (keys_tail->anchor.abs_time, 717 duration), 718 lookahead_sign), 719 overlap_duration)) ) 720 { 721 if (! wake) 722 { 723 key_gen++; 724 wake = true; 725 } 726 if (GNUNET_OK != 727 create_key ()) 728 { 729 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 730 GNUNET_break (0); 731 globals->global_ret = EXIT_FAILURE; 732 GNUNET_SCHEDULER_shutdown (); 733 return; 734 } 735 } 736 nxt = keys_head; 737 /* purge expired keys */ 738 while ( (NULL != nxt) && 739 GNUNET_TIME_absolute_is_past ( 740 GNUNET_TIME_absolute_add (nxt->anchor.abs_time, 741 duration))) 742 { 743 if (! wake) 744 { 745 key_gen++; 746 wake = true; 747 } 748 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 749 "Purging past key %s (expired %s ago)\n", 750 TALER_B2S (&nxt->exchange_pub), 751 GNUNET_TIME_relative2s ( 752 GNUNET_TIME_absolute_get_duration ( 753 GNUNET_TIME_absolute_add (nxt->anchor.abs_time, 754 duration)), 755 GNUNET_YES)); 756 purge_key (nxt); 757 nxt = nxt->next; 758 } 759 at = key_action_time (); 760 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 761 if (wake) 762 TES_wake_clients (); 763 keygen_task = GNUNET_SCHEDULER_add_at (at, 764 &update_keys, 765 NULL); 766 } 767 768 769 /** 770 * Parse private key from @a filename in @a buf. 771 * 772 * @param filename name of the file we are parsing, for logging 773 * @param buf key material 774 * @param buf_size number of bytes in @a buf 775 * @return #GNUNET_OK on success 776 */ 777 static enum GNUNET_GenericReturnValue 778 parse_key (const char *filename, 779 const void *buf, 780 size_t buf_size) 781 { 782 struct GNUNET_CRYPTO_EddsaPrivateKey priv; 783 char *anchor_s; 784 char dummy; 785 unsigned long long anchor_ll; 786 struct GNUNET_TIME_Timestamp anchor; 787 788 anchor_s = strrchr (filename, 789 '/'); 790 if (NULL == anchor_s) 791 { 792 /* File in a directory without '/' in the name, this makes no sense. */ 793 GNUNET_break (0); 794 return GNUNET_SYSERR; 795 } 796 anchor_s++; 797 if (1 != sscanf (anchor_s, 798 "%llu%c", 799 &anchor_ll, 800 &dummy)) 801 { 802 /* Filenames in KEYDIR must ONLY be the anchor time in seconds! */ 803 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 804 "Filename `%s' invalid for key file, skipping\n", 805 filename); 806 return GNUNET_SYSERR; 807 } 808 anchor.abs_time.abs_value_us = anchor_ll 809 * GNUNET_TIME_UNIT_SECONDS.rel_value_us; 810 if (anchor_ll != anchor.abs_time.abs_value_us 811 / GNUNET_TIME_UNIT_SECONDS.rel_value_us) 812 { 813 /* Integer overflow. Bad, invalid filename. */ 814 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 815 "Filename `%s' invalid for key file, skipping\n", 816 filename); 817 return GNUNET_SYSERR; 818 } 819 if (buf_size != sizeof (priv)) 820 { 821 /* Parser failure. */ 822 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 823 "File `%s' is malformed, skipping\n", 824 filename); 825 return GNUNET_SYSERR; 826 } 827 GNUNET_memcpy (&priv, 828 buf, 829 buf_size); 830 831 { 832 struct GNUNET_CRYPTO_EddsaPublicKey pub; 833 struct Key *key; 834 struct Key *before; 835 836 GNUNET_CRYPTO_eddsa_key_get_public (&priv, 837 &pub); 838 GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); 839 key = GNUNET_new (struct Key); 840 key->exchange_priv.eddsa_priv = priv; 841 key->exchange_pub.eddsa_pub = pub; 842 key->anchor = anchor; 843 key->filename = GNUNET_strdup (filename); 844 key->key_gen = key_gen; 845 before = NULL; 846 for (struct Key *pos = keys_head; 847 NULL != pos; 848 pos = pos->next) 849 { 850 if (GNUNET_TIME_timestamp_cmp (pos->anchor, >, anchor)) 851 break; 852 before = pos; 853 } 854 GNUNET_CONTAINER_DLL_insert_after (keys_head, 855 keys_tail, 856 before, 857 key); 858 GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); 859 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 860 "Imported key from `%s'\n", 861 filename); 862 } 863 return GNUNET_OK; 864 } 865 866 867 /** 868 * Import a private key from @a filename. 869 * 870 * @param cls NULL 871 * @param filename name of a file in the directory 872 */ 873 static enum GNUNET_GenericReturnValue 874 import_key (void *cls, 875 const char *filename) 876 { 877 struct GNUNET_DISK_FileHandle *fh; 878 struct GNUNET_DISK_MapHandle *map; 879 void *ptr; 880 int fd; 881 struct stat sbuf; 882 883 (void) cls; 884 { 885 struct stat lsbuf; 886 887 if (0 != lstat (filename, 888 &lsbuf)) 889 { 890 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 891 "lstat", 892 filename); 893 return GNUNET_OK; 894 } 895 if (! S_ISREG (lsbuf.st_mode)) 896 { 897 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 898 "File `%s' is not a regular file, which is not allowed for private keys!\n", 899 filename); 900 return GNUNET_OK; 901 } 902 } 903 904 fd = open (filename, 905 O_RDONLY | O_CLOEXEC); 906 if (-1 == fd) 907 { 908 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 909 "open", 910 filename); 911 return GNUNET_OK; 912 } 913 if (0 != fstat (fd, 914 &sbuf)) 915 { 916 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 917 "stat", 918 filename); 919 GNUNET_break (0 == close (fd)); 920 return GNUNET_OK; 921 } 922 if (! S_ISREG (sbuf.st_mode)) 923 { 924 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 925 "File `%s' is not a regular file, which is not allowed for private keys!\n", 926 filename); 927 GNUNET_break (0 == close (fd)); 928 return GNUNET_OK; 929 } 930 if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO))) 931 { 932 /* permission are NOT tight, try to patch them up! */ 933 if (0 != 934 fchmod (fd, 935 S_IRUSR)) 936 { 937 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 938 "fchmod", 939 filename); 940 /* refuse to use key if file has wrong permissions */ 941 GNUNET_break (0 == close (fd)); 942 return GNUNET_OK; 943 } 944 } 945 fh = GNUNET_DISK_get_handle_from_int_fd (fd); 946 if (NULL == fh) 947 { 948 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 949 "open", 950 filename); 951 GNUNET_break (0 == close (fd)); 952 return GNUNET_OK; 953 } 954 if (sbuf.st_size > 2048) 955 { 956 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 957 "File `%s' to big to be a private key\n", 958 filename); 959 GNUNET_DISK_file_close (fh); 960 return GNUNET_OK; 961 } 962 ptr = GNUNET_DISK_file_map (fh, 963 &map, 964 GNUNET_DISK_MAP_TYPE_READ, 965 (size_t) sbuf.st_size); 966 if (NULL == ptr) 967 { 968 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, 969 "mmap", 970 filename); 971 GNUNET_DISK_file_close (fh); 972 return GNUNET_OK; 973 } 974 (void) parse_key (filename, 975 ptr, 976 (size_t) sbuf.st_size); 977 GNUNET_DISK_file_unmap (map); 978 GNUNET_DISK_file_close (fh); 979 return GNUNET_OK; 980 } 981 982 983 /** 984 * Load the various duration values from @a kcfg. 985 * 986 * @param cfg configuration to use 987 * @return #GNUNET_OK on success 988 */ 989 static enum GNUNET_GenericReturnValue 990 load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg) 991 { 992 char *secname; 993 994 GNUNET_asprintf (&secname, 995 "%s-secmod-eddsa", 996 globals->section); 997 if (GNUNET_OK != 998 GNUNET_CONFIGURATION_get_value_time (cfg, 999 secname, 1000 "OVERLAP_DURATION", 1001 &overlap_duration)) 1002 { 1003 GNUNET_free (secname); 1004 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1005 secname, 1006 "OVERLAP_DURATION"); 1007 return GNUNET_SYSERR; 1008 } 1009 if (GNUNET_OK != 1010 GNUNET_CONFIGURATION_get_value_time (cfg, 1011 secname, 1012 "DURATION", 1013 &duration)) 1014 { 1015 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1016 secname, 1017 "DURATION"); 1018 GNUNET_free (secname); 1019 return GNUNET_SYSERR; 1020 } 1021 if (GNUNET_OK != 1022 GNUNET_CONFIGURATION_get_value_time (cfg, 1023 secname, 1024 "LOOKAHEAD_SIGN", 1025 &lookahead_sign)) 1026 { 1027 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1028 secname, 1029 "LOOKAHEAD_SIGN"); 1030 GNUNET_free (secname); 1031 return GNUNET_SYSERR; 1032 } 1033 GNUNET_free (secname); 1034 return GNUNET_OK; 1035 } 1036 1037 1038 /** 1039 * Function run on shutdown. Stops the various jobs (nicely). 1040 * 1041 * @param cls NULL 1042 */ 1043 static void 1044 do_shutdown (void *cls) 1045 { 1046 (void) cls; 1047 TES_listen_stop (); 1048 if (NULL != keygen_task) 1049 { 1050 GNUNET_SCHEDULER_cancel (keygen_task); 1051 keygen_task = NULL; 1052 } 1053 } 1054 1055 1056 void 1057 TALER_SECMOD_eddsa_run (void *cls, 1058 char *const *args, 1059 const char *cfgfile, 1060 const struct GNUNET_CONFIGURATION_Handle *cfg) 1061 { 1062 static struct TES_Callbacks cb = { 1063 .dispatch = eddsa_work_dispatch, 1064 .updater = eddsa_update_client_keys, 1065 .init = eddsa_client_init 1066 }; 1067 struct TALER_SECMOD_Options *opt = cls; 1068 char *secname; 1069 1070 (void) args; 1071 (void) cfgfile; 1072 globals = opt; 1073 if (GNUNET_TIME_timestamp_cmp (opt->global_now, 1074 !=, 1075 opt->global_now_tmp)) 1076 { 1077 /* The user gave "--now", use it! */ 1078 opt->global_now = opt->global_now_tmp; 1079 } 1080 else 1081 { 1082 /* get current time again, we may be timetraveling! */ 1083 opt->global_now = GNUNET_TIME_timestamp_get (); 1084 } 1085 if (GNUNET_OK != 1086 load_durations (cfg)) 1087 { 1088 opt->global_ret = EXIT_NOTCONFIGURED; 1089 return; 1090 } 1091 GNUNET_asprintf (&secname, 1092 "%s-secmod-eddsa", 1093 opt->section); 1094 if (GNUNET_OK != 1095 GNUNET_CONFIGURATION_get_value_filename (cfg, 1096 secname, 1097 "KEY_DIR", 1098 &keydir)) 1099 { 1100 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1101 secname, 1102 "KEY_DIR"); 1103 GNUNET_free (secname); 1104 opt->global_ret = EXIT_NOTCONFIGURED; 1105 return; 1106 } 1107 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 1108 NULL); 1109 opt->global_ret = TES_listen_start (cfg, 1110 secname, 1111 &cb); 1112 GNUNET_free (secname); 1113 if (0 != opt->global_ret) 1114 return; 1115 /* Load keys */ 1116 GNUNET_break (GNUNET_OK == 1117 GNUNET_DISK_directory_create (keydir)); 1118 GNUNET_DISK_directory_scan (keydir, 1119 &import_key, 1120 NULL); 1121 if ( (NULL != keys_head) && 1122 (GNUNET_TIME_absolute_is_future (keys_head->anchor.abs_time)) ) 1123 { 1124 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1125 "Existing anchor is in %s the future. Refusing to start\n", 1126 GNUNET_TIME_relative2s ( 1127 GNUNET_TIME_absolute_get_remaining ( 1128 keys_head->anchor.abs_time), 1129 true)); 1130 opt->global_ret = EXIT_FAILURE; 1131 GNUNET_SCHEDULER_shutdown (); 1132 return; 1133 } 1134 /* start job to keep keys up-to-date; MUST be run before the #listen_task, 1135 hence with priority. */ 1136 keygen_task = GNUNET_SCHEDULER_add_with_priority ( 1137 GNUNET_SCHEDULER_PRIORITY_URGENT, 1138 &update_keys, 1139 NULL); 1140 }