taler-exchange-httpd_get-keys.c (99616B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2020-2025 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero 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 Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file taler-exchange-httpd_get-keys.c 18 * @brief management of our various keys 19 * @author Christian Grothoff 20 * @author Özgür Kesim 21 */ 22 #include "taler/taler_json_lib.h" 23 #include "taler/taler_mhd_lib.h" 24 #include "taler/taler_kyclogic_lib.h" 25 #include "taler/taler_dbevents.h" 26 #include "taler-exchange-httpd.h" 27 #include "exchange-database/preflight.h" 28 #include "taler-exchange-httpd_get-config.h" 29 #include "taler-exchange-httpd_get-keys.h" 30 #include "taler-exchange-httpd_secmod-helpers.h" 31 #include "taler-exchange-httpd_responses.h" 32 #include "taler-exchange-httpd_configuration.h" 33 #include "exchange-database/event_listen.h" 34 #include "exchange-database/event_listen_cancel.h" 35 #include "exchange-database/event_notify.h" 36 struct TEH_KeyStateHandle; 37 struct AddContext; 38 #define TALER_EXCHANGEDB_GLOBAL_FEE_RESULT_CLOSURE struct TEH_KeyStateHandle 39 #define TALER_EXCHANGEDB_WIRE_ACCOUNT_RESULT_CLOSURE json_t 40 #define TALER_EXCHANGEDB_WIRE_FEE_RESULT_CLOSURE struct AddContext 41 #define TALER_EXCHANGEDB_AUDITORS_RESULT_CLOSURE struct TEH_KeyStateHandle 42 #define TALER_EXCHANGEDB_ACTIVE_SIGNKEYS_RESULT_CLOSURE struct \ 43 TEH_KeyStateHandle 44 #define TALER_EXCHANGEDB_AUDITOR_DENOMINATIONS_RESULT_CLOSURE struct \ 45 TEH_KeyStateHandle 46 #define TALER_EXCHANGEDB_DENOMINATIONS_RESULT_CLOSURE struct TEH_KeyStateHandle 47 #include "exchange-database/iterate_global_fees.h" 48 #include "exchange-database/iterate_wire_accounts.h" 49 #include "exchange-database/iterate_wire_fees.h" 50 #include "exchange-database/iterate_active_auditors.h" 51 #include "exchange-database/iterate_active_signkeys.h" 52 #include "exchange-database/iterate_auditor_denominations.h" 53 #include "exchange-database/iterate_denominations.h" 54 55 56 /** 57 * How many /keys request do we hold in suspension at 58 * most at any time? 59 */ 60 #define SKR_LIMIT 32 61 62 63 /** 64 * When do we forcefully timeout a /keys request? 65 * Matches the 120s hard-coded into exchange_api_handle.c 66 */ 67 #define KEYS_TIMEOUT \ 68 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2) 69 70 71 /** 72 * Signatures of an auditor over a denomination key of this exchange. 73 */ 74 struct TEH_AuditorSignature 75 { 76 /** 77 * We store the signatures in a DLL. 78 */ 79 struct TEH_AuditorSignature *prev; 80 81 /** 82 * We store the signatures in a DLL. 83 */ 84 struct TEH_AuditorSignature *next; 85 86 /** 87 * A signature from the auditor. 88 */ 89 struct TALER_AuditorSignatureP asig; 90 91 /** 92 * Public key of the auditor. 93 */ 94 struct TALER_AuditorPublicKeyP apub; 95 96 }; 97 98 99 /** 100 * Entry in (sorted) array with possible pre-build responses for /keys. 101 * We keep pre-build responses for the various (valid) cherry-picking 102 * values around. 103 */ 104 struct KeysResponseData 105 { 106 107 /** 108 * Response to return if the client supports (deflate) compression. 109 */ 110 struct MHD_Response *response_compressed; 111 112 /** 113 * Response to return if the client does not support compression. 114 */ 115 struct MHD_Response *response_uncompressed; 116 117 /** 118 * ETag for these responses. 119 */ 120 char *etag; 121 122 /** 123 * Cherry-picking timestamp the client must have set for this 124 * response to be valid. 0 if this is the "full" response. 125 * The client's request must include this date or a higher one 126 * for this response to be applicable. 127 */ 128 struct GNUNET_TIME_Timestamp cherry_pick_date; 129 130 }; 131 132 133 /** 134 * @brief All information about an exchange online signing key (which is used to 135 * sign messages from the exchange). 136 */ 137 struct SigningKey 138 { 139 140 /** 141 * The exchange's (online signing) public key. 142 */ 143 struct TALER_ExchangePublicKeyP exchange_pub; 144 145 /** 146 * Meta data about the signing key, such as validity periods. 147 */ 148 struct TALER_EXCHANGEDB_SignkeyMetaData meta; 149 150 /** 151 * The long-term offline master key's signature for this signing key. 152 * Signs over @e exchange_pub and @e meta. 153 */ 154 struct TALER_MasterSignatureP master_sig; 155 156 }; 157 158 struct TEH_KeyStateHandle 159 { 160 161 /** 162 * Mapping from denomination keys to denomination key issue struct. 163 * Used to lookup the key by hash. 164 */ 165 struct GNUNET_CONTAINER_MultiHashMap *denomkey_map; 166 167 /** 168 * Mapping from serial ID's to denomination key issue struct. 169 * Used to lookup the key by serial ID. 170 * 171 * FIXME: We need a 64-bit version of this in GNUNET. 172 */ 173 struct GNUNET_CONTAINER_MultiHashMap32 *denomserial_map; 174 175 /** 176 * Map from `struct TALER_ExchangePublicKey` to `struct SigningKey` 177 * entries. Based on the fact that a `struct GNUNET_PeerIdentity` is also 178 * an EdDSA public key. 179 */ 180 struct GNUNET_CONTAINER_MultiPeerMap *signkey_map; 181 182 /** 183 * Head of DLL of our global fees. 184 */ 185 struct TEH_GlobalFee *gf_head; 186 187 /** 188 * Tail of DLL of our global fees. 189 */ 190 struct TEH_GlobalFee *gf_tail; 191 192 /** 193 * json array with the auditors of this exchange. Contains exactly 194 * the information needed for the "auditors" field of the /keys response. 195 */ 196 json_t *auditors; 197 198 /** 199 * json array with the global fees of this exchange. Contains exactly 200 * the information needed for the "global_fees" field of the /keys response. 201 */ 202 json_t *global_fees; 203 204 /** 205 * Sorted array of responses to /keys (MUST be sorted by cherry-picking date) of 206 * length @e krd_array_length; 207 */ 208 struct KeysResponseData *krd_array; 209 210 /** 211 * Length of the @e krd_array. 212 */ 213 unsigned int krd_array_length; 214 215 /** 216 * Cached reply for a GET /management/keys request. Used so we do not 217 * re-create the reply every time. 218 */ 219 json_t *management_keys_reply; 220 221 /** 222 * For which (global) key_generation was this data structure created? 223 * Used to check when we are outdated and need to be re-generated. 224 */ 225 uint64_t key_generation; 226 227 /** 228 * When did we initiate the key reloading? 229 */ 230 struct GNUNET_TIME_Timestamp reload_time; 231 232 /** 233 * What is the period at which we rotate keys 234 * (signing or denomination keys)? 235 */ 236 struct GNUNET_TIME_Relative rekey_frequency; 237 238 /** 239 * When does our online signing key expire and we 240 * thus need to re-generate this response? 241 */ 242 struct GNUNET_TIME_Timestamp signature_expires; 243 244 /** 245 * True if #finish_keys_response() was not yet run and this key state 246 * is only suitable for the /management/keys API. 247 */ 248 bool management_only; 249 250 }; 251 252 253 /** 254 * Entry of /keys requests that are currently suspended because we are 255 * waiting for /keys to become ready. 256 */ 257 struct SuspendedKeysRequests 258 { 259 /** 260 * Kept in a DLL. 261 */ 262 struct SuspendedKeysRequests *next; 263 264 /** 265 * Kept in a DLL. 266 */ 267 struct SuspendedKeysRequests *prev; 268 269 /** 270 * The suspended connection. 271 */ 272 struct MHD_Connection *connection; 273 274 /** 275 * When does this request timeout? 276 */ 277 struct GNUNET_TIME_Absolute timeout; 278 }; 279 280 281 /** 282 * Information we track about wire fees. 283 */ 284 struct WireFeeSet 285 { 286 287 /** 288 * Kept in a DLL. 289 */ 290 struct WireFeeSet *next; 291 292 /** 293 * Kept in a DLL. 294 */ 295 struct WireFeeSet *prev; 296 297 /** 298 * Actual fees. 299 */ 300 struct TALER_WireFeeSet fees; 301 302 /** 303 * Start date of fee validity (inclusive). 304 */ 305 struct GNUNET_TIME_Timestamp start_date; 306 307 /** 308 * End date of fee validity (exclusive). 309 */ 310 struct GNUNET_TIME_Timestamp end_date; 311 312 /** 313 * Wire method the fees apply to. 314 */ 315 char *method; 316 }; 317 318 319 /** 320 * State we keep per thread to cache the wire part of the /keys response. 321 */ 322 struct WireStateHandle 323 { 324 325 /** 326 * JSON reply for wire response. 327 */ 328 json_t *json_reply; 329 330 /** 331 * ETag for this response (if any). 332 */ 333 char *etag; 334 335 /** 336 * head of DLL of wire fees. 337 */ 338 struct WireFeeSet *wfs_head; 339 340 /** 341 * Tail of DLL of wire fees. 342 */ 343 struct WireFeeSet *wfs_tail; 344 345 /** 346 * Earliest timestamp of all the wire methods when we have no more fees. 347 */ 348 struct GNUNET_TIME_Absolute cache_expiration; 349 350 /** 351 * @e cache_expiration time, formatted. 352 */ 353 char dat[128]; 354 355 /** 356 * For which (global) wire_generation was this data structure created? 357 * Used to check when we are outdated and need to be re-generated. 358 */ 359 uint64_t wire_generation; 360 361 /** 362 * Is the wire data ready? 363 */ 364 bool ready; 365 366 }; 367 368 369 /** 370 * Stores the latest generation of our wire response. 371 */ 372 static struct WireStateHandle *wire_state; 373 374 /** 375 * Handler listening for wire updates by other exchange 376 * services. 377 */ 378 static struct GNUNET_DB_EventHandler *wire_eh; 379 380 /** 381 * Counter incremented whenever we have a reason to re-build the #wire_state 382 * because something external changed. 383 */ 384 static uint64_t wire_generation; 385 386 /** 387 * Counter incremented whenever we have a reason to re-build the keys because 388 * something external changed. See #TEH_keys_get_state() and 389 * #TEH_keys_update_states() for uses of this variable. 390 */ 391 static uint64_t key_generation; 392 393 /** 394 * Stores the latest generation of our key state. 395 */ 396 static struct TEH_KeyStateHandle *key_state; 397 398 /** 399 * Handler listening for wire updates by other exchange 400 * services. 401 */ 402 static struct GNUNET_DB_EventHandler *keys_eh; 403 404 /** 405 * Head of DLL of suspended /keys requests. 406 */ 407 static struct SuspendedKeysRequests *skr_head; 408 409 /** 410 * Tail of DLL of suspended /keys requests. 411 */ 412 static struct SuspendedKeysRequests *skr_tail; 413 414 /** 415 * Number of entries in the @e skr_head DLL. 416 */ 417 static unsigned int skr_size; 418 419 /** 420 * Task to force timeouts on /keys requests. 421 */ 422 static struct GNUNET_SCHEDULER_Task *keys_tt; 423 424 /** 425 * For how long should a signing key be legally retained? 426 * Configuration value. 427 */ 428 static struct GNUNET_TIME_Relative signkey_legal_duration; 429 430 /** 431 * What type of asset are we dealing with here? 432 */ 433 static char *asset_type; 434 435 /** 436 * Are we shutting down? 437 */ 438 static bool terminating; 439 440 441 /** 442 * Free memory associated with @a wsh 443 * 444 * @param[in] wsh wire state to destroy 445 */ 446 static void 447 destroy_wire_state (struct WireStateHandle *wsh) 448 { 449 struct WireFeeSet *wfs; 450 451 while (NULL != (wfs = wsh->wfs_head)) 452 { 453 GNUNET_CONTAINER_DLL_remove (wsh->wfs_head, 454 wsh->wfs_tail, 455 wfs); 456 GNUNET_free (wfs->method); 457 GNUNET_free (wfs); 458 } 459 json_decref (wsh->json_reply); 460 GNUNET_free (wsh->etag); 461 GNUNET_free (wsh); 462 } 463 464 465 /** 466 * Function called whenever another exchange process has updated 467 * the wire data in the database. 468 * 469 * @param cls NULL 470 * @param extra unused 471 * @param extra_size number of bytes in @a extra unused 472 */ 473 static void 474 wire_update_event_cb (void *cls, 475 const void *extra, 476 size_t extra_size) 477 { 478 (void) cls; 479 (void) extra; 480 (void) extra_size; 481 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 482 "Received wire update event\n"); 483 TEH_check_invariants (); 484 wire_generation++; 485 key_generation++; 486 TEH_resume_keys_requests (false); 487 } 488 489 490 /** 491 * Add information about a wire account to @a a. 492 * 493 * @param a a `json_t *` array to expand with wire account details 494 * @param payto_uri the exchange bank account URI to add 495 * @param conversion_url URL of a conversion service, NULL if there is no conversion 496 * @param open_banking_gateway URL of an open banking gateway, NULL if there is none 497 * @param prepared_transfer_url URL of a wire transfer gateway, NULL if there is none 498 * @param debit_restrictions JSON array with debit restrictions on the account 499 * @param credit_restrictions JSON array with credit restrictions on the account 500 * @param master_sig master key signature affirming that this is a bank 501 * account of the exchange (of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS) 502 * @param bank_label label the wallet should use to display the account, can be NULL 503 * @param priority priority for ordering bank account labels 504 */ 505 static void 506 add_wire_account ( 507 json_t *a, 508 const struct TALER_FullPayto payto_uri, 509 const char *conversion_url, 510 const char *open_banking_gateway, 511 const char *prepared_transfer_url, 512 const json_t *debit_restrictions, 513 const json_t *credit_restrictions, 514 const struct TALER_MasterSignatureP *master_sig, 515 const char *bank_label, 516 int64_t priority) 517 { 518 if (GNUNET_OK != 519 TALER_exchange_wire_signature_check ( 520 payto_uri, 521 conversion_url, 522 open_banking_gateway, 523 prepared_transfer_url, 524 debit_restrictions, 525 credit_restrictions, 526 &TEH_master_public_key, 527 master_sig)) 528 { 529 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 530 "Database has wire account with invalid signature. Skipping entry. Did the exchange offline public key change?\n"); 531 return; 532 } 533 if (0 != 534 json_array_append_new ( 535 a, 536 GNUNET_JSON_PACK ( 537 TALER_JSON_pack_full_payto ( 538 "payto_uri", 539 payto_uri), 540 GNUNET_JSON_pack_allow_null ( 541 GNUNET_JSON_pack_string ( 542 "conversion_url", 543 conversion_url)), 544 GNUNET_JSON_pack_allow_null ( 545 GNUNET_JSON_pack_string ( 546 "open_banking_gateway", 547 open_banking_gateway)), 548 GNUNET_JSON_pack_allow_null ( 549 GNUNET_JSON_pack_string ( 550 "prepared_transfer_url", 551 prepared_transfer_url)), 552 GNUNET_JSON_pack_allow_null ( 553 GNUNET_JSON_pack_string ( 554 "bank_label", 555 bank_label)), 556 GNUNET_JSON_pack_int64 ( 557 "priority", 558 priority), 559 GNUNET_JSON_pack_array_incref ( 560 "debit_restrictions", 561 (json_t *) debit_restrictions), 562 GNUNET_JSON_pack_array_incref ( 563 "credit_restrictions", 564 (json_t *) credit_restrictions), 565 GNUNET_JSON_pack_data_auto ( 566 "master_sig", 567 master_sig)))) 568 { 569 GNUNET_break (0); /* out of memory!? */ 570 return; 571 } 572 } 573 574 575 /** 576 * Closure for #add_wire_fee(). 577 */ 578 struct AddContext 579 { 580 /** 581 * Wire method the fees are for. 582 */ 583 char *wire_method; 584 585 /** 586 * Wire state we are building. 587 */ 588 struct WireStateHandle *wsh; 589 590 /** 591 * Array to append the fee to. 592 */ 593 json_t *a; 594 595 /** 596 * Set to the maximum end-date seen. 597 */ 598 struct GNUNET_TIME_Absolute max_seen; 599 }; 600 601 602 /** 603 * Add information about a wire account to @a ac. 604 * 605 * @param ac a `struct AddContext` 606 * @param fees the wire fees we charge 607 * @param start_date from when are these fees valid (start date) 608 * @param end_date until when are these fees valid (end date, exclusive) 609 * @param master_sig master key signature affirming that this is the correct 610 * fee (of purpose #TALER_SIGNATURE_MASTER_WIRE_FEES) 611 */ 612 static void 613 add_wire_fee (struct AddContext *ac, 614 const struct TALER_WireFeeSet *fees, 615 struct GNUNET_TIME_Timestamp start_date, 616 struct GNUNET_TIME_Timestamp end_date, 617 const struct TALER_MasterSignatureP *master_sig) 618 { 619 struct WireFeeSet *wfs; 620 621 if (GNUNET_OK != 622 TALER_exchange_offline_wire_fee_verify ( 623 ac->wire_method, 624 start_date, 625 end_date, 626 fees, 627 &TEH_master_public_key, 628 master_sig)) 629 { 630 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 631 "Database has wire fee with invalid signature. Skipping entry. Did the exchange offline public key change?\n"); 632 return; 633 } 634 ac->max_seen = GNUNET_TIME_absolute_max (ac->max_seen, 635 end_date.abs_time); 636 wfs = GNUNET_new (struct WireFeeSet); 637 wfs->start_date = start_date; 638 wfs->end_date = end_date; 639 wfs->fees = *fees; 640 wfs->method = GNUNET_strdup (ac->wire_method); 641 GNUNET_CONTAINER_DLL_insert (ac->wsh->wfs_head, 642 ac->wsh->wfs_tail, 643 wfs); 644 if (0 != 645 json_array_append_new ( 646 ac->a, 647 GNUNET_JSON_PACK ( 648 TALER_JSON_pack_amount ("wire_fee", 649 &fees->wire), 650 TALER_JSON_pack_amount ("closing_fee", 651 &fees->closing), 652 GNUNET_JSON_pack_timestamp ("start_date", 653 start_date), 654 GNUNET_JSON_pack_timestamp ("end_date", 655 end_date), 656 GNUNET_JSON_pack_data_auto ("sig", 657 master_sig)))) 658 { 659 GNUNET_break (0); /* out of memory!? */ 660 return; 661 } 662 } 663 664 665 /** 666 * Create the wire response from our database state. 667 * 668 * @return NULL on error 669 */ 670 static struct WireStateHandle * 671 build_wire_state (void) 672 { 673 json_t *wire_accounts_array; 674 json_t *wire_fee_object; 675 uint64_t wg = wire_generation; /* must be obtained FIRST */ 676 enum GNUNET_DB_QueryStatus qs; 677 struct WireStateHandle *wsh; 678 json_t *wads; 679 680 wsh = GNUNET_new (struct WireStateHandle); 681 wsh->wire_generation = wg; 682 wire_accounts_array = json_array (); 683 GNUNET_assert (NULL != wire_accounts_array); 684 qs = TALER_EXCHANGEDB_iterate_wire_accounts (TEH_pg, 685 &add_wire_account, 686 wire_accounts_array); 687 if (0 > qs) 688 { 689 GNUNET_break (0); 690 json_decref (wire_accounts_array); 691 wsh->ready = false; 692 return wsh; 693 } 694 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 695 "Built wire data with %u accounts (%d)\n", 696 (unsigned int) json_array_size (wire_accounts_array), 697 (int) qs); 698 wire_fee_object = json_object (); 699 GNUNET_assert (NULL != wire_fee_object); 700 wsh->cache_expiration = GNUNET_TIME_UNIT_FOREVER_ABS; 701 { 702 json_t *account; 703 size_t index; 704 705 json_array_foreach (wire_accounts_array, 706 index, 707 account) 708 { 709 char *wire_method; 710 const char *payto_uri = json_string_value (json_object_get (account, 711 "payto_uri")); 712 713 GNUNET_assert (NULL != payto_uri); 714 wire_method = TALER_payto_get_method (payto_uri); 715 if (NULL == wire_method) 716 { 717 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 718 "No wire method in `%s'\n", 719 payto_uri); 720 wsh->ready = false; 721 json_decref (wire_accounts_array); 722 json_decref (wire_fee_object); 723 return wsh; 724 } 725 if (NULL == json_object_get (wire_fee_object, 726 wire_method)) 727 { 728 struct AddContext ac = { 729 .wire_method = wire_method, 730 .wsh = wsh, 731 .a = json_array () 732 }; 733 734 GNUNET_assert (NULL != ac.a); 735 qs = TALER_EXCHANGEDB_iterate_wire_fees (TEH_pg, 736 wire_method, 737 &add_wire_fee, 738 &ac); 739 if (0 > qs) 740 { 741 GNUNET_break (0); 742 json_decref (ac.a); 743 json_decref (wire_fee_object); 744 json_decref (wire_accounts_array); 745 GNUNET_free (wire_method); 746 wsh->ready = false; 747 return wsh; 748 } 749 if (0 != json_array_size (ac.a)) 750 { 751 wsh->cache_expiration 752 = GNUNET_TIME_absolute_min (ac.max_seen, 753 wsh->cache_expiration); 754 GNUNET_assert (0 == 755 json_object_set_new (wire_fee_object, 756 wire_method, 757 ac.a)); 758 } 759 else 760 { 761 json_decref (ac.a); 762 } 763 } 764 GNUNET_free (wire_method); 765 } 766 } 767 768 wads = json_array (); /* #7271 */ 769 GNUNET_assert (NULL != wads); 770 wsh->json_reply = GNUNET_JSON_PACK ( 771 GNUNET_JSON_pack_array_steal ("accounts", 772 wire_accounts_array), 773 GNUNET_JSON_pack_array_steal ("wads", 774 wads), 775 GNUNET_JSON_pack_object_steal ("fees", 776 wire_fee_object)); 777 wsh->ready = true; 778 return wsh; 779 } 780 781 782 void 783 TEH_wire_update_state (void) 784 { 785 struct GNUNET_DB_EventHeaderP es = { 786 .size = htons (sizeof (es)), 787 .type = htons (TALER_DBEVENT_EXCHANGE_WIRE_UPDATED), 788 }; 789 790 TALER_EXCHANGEDB_event_notify (TEH_pg, 791 &es, 792 NULL, 793 0); 794 wire_generation++; 795 key_generation++; 796 } 797 798 799 void 800 TEH_keys_bump_generation (void) 801 { 802 key_generation++; 803 } 804 805 806 /** 807 * Return the current key state for this thread. Possibly 808 * re-builds the key state if we have reason to believe 809 * that something changed. 810 * 811 * @return NULL on error 812 */ 813 static struct WireStateHandle * 814 get_wire_state (void) 815 { 816 struct WireStateHandle *old_wsh; 817 818 old_wsh = wire_state; 819 if ( (NULL == old_wsh) || 820 (old_wsh->wire_generation < wire_generation) ) 821 { 822 struct WireStateHandle *wsh; 823 824 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 825 "Rebuilding wire, generation upgrade from %llu to %llu\n", 826 (unsigned long long) (NULL == old_wsh) ? 0LL : 827 old_wsh->wire_generation, 828 (unsigned long long) wire_generation); 829 TEH_check_invariants (); 830 wsh = build_wire_state (); 831 wire_state = wsh; 832 if (NULL != old_wsh) 833 destroy_wire_state (old_wsh); 834 TEH_check_invariants (); 835 return wsh; 836 } 837 return old_wsh; 838 } 839 840 841 const struct TALER_WireFeeSet * 842 TEH_wire_fees_by_time ( 843 struct GNUNET_TIME_Timestamp ts, 844 const char *method) 845 { 846 struct WireStateHandle *wsh = get_wire_state (); 847 848 for (struct WireFeeSet *wfs = wsh->wfs_head; 849 NULL != wfs; 850 wfs = wfs->next) 851 { 852 if (0 != strcmp (method, 853 wfs->method)) 854 continue; 855 if ( (GNUNET_TIME_timestamp_cmp (wfs->start_date, 856 >, 857 ts)) || 858 (GNUNET_TIME_timestamp_cmp (ts, 859 >=, 860 wfs->end_date)) ) 861 continue; 862 return &wfs->fees; 863 } 864 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 865 "No wire fees for method `%s' at %s configured\n", 866 method, 867 GNUNET_TIME_timestamp2s (ts)); 868 return NULL; 869 } 870 871 872 /** 873 * Function called to forcefully resume suspended keys requests. 874 * 875 * @param cls unused, NULL 876 */ 877 static void 878 keys_timeout_cb (void *cls) 879 { 880 struct SuspendedKeysRequests *skr; 881 882 (void) cls; 883 keys_tt = NULL; 884 while (NULL != (skr = skr_head)) 885 { 886 if (GNUNET_TIME_absolute_is_future (skr->timeout)) 887 break; 888 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 889 "Resuming /keys request due to timeout\n"); 890 GNUNET_CONTAINER_DLL_remove (skr_head, 891 skr_tail, 892 skr); 893 skr_size--; 894 MHD_resume_connection (skr->connection); 895 TALER_MHD_daemon_trigger (); 896 GNUNET_free (skr); 897 } 898 if (NULL == skr) 899 return; 900 keys_tt = GNUNET_SCHEDULER_add_at (skr->timeout, 901 &keys_timeout_cb, 902 NULL); 903 } 904 905 906 /** 907 * Suspend /keys request while we (hopefully) are waiting to be 908 * provisioned with key material. 909 * 910 * @param[in] connection to suspend 911 */ 912 static enum MHD_Result 913 suspend_request (struct MHD_Connection *connection) 914 { 915 struct SuspendedKeysRequests *skr; 916 917 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 918 "Suspending /keys request until key material changes\n"); 919 if (terminating) 920 { 921 return TALER_MHD_reply_with_error (connection, 922 MHD_HTTP_SERVICE_UNAVAILABLE, 923 TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, 924 "Exchange terminating"); 925 } 926 skr = GNUNET_new (struct SuspendedKeysRequests); 927 skr->connection = connection; 928 MHD_suspend_connection (connection); 929 GNUNET_CONTAINER_DLL_insert (skr_head, 930 skr_tail, 931 skr); 932 skr_size++; 933 skr->timeout = GNUNET_TIME_relative_to_absolute (KEYS_TIMEOUT); 934 if (NULL == keys_tt) 935 { 936 keys_tt = GNUNET_SCHEDULER_add_at (skr->timeout, 937 &keys_timeout_cb, 938 NULL); 939 } 940 while (skr_size > SKR_LIMIT) 941 { 942 skr = skr_tail; 943 GNUNET_CONTAINER_DLL_remove (skr_head, 944 skr_tail, 945 skr); 946 skr_size--; 947 MHD_resume_connection (skr->connection); 948 TALER_MHD_daemon_trigger (); 949 GNUNET_free (skr); 950 } 951 return MHD_YES; 952 } 953 954 955 /** 956 * Called on each denomination key. Checks that the key still works. 957 * 958 * @param cls NULL 959 * @param hc denomination hash (unused) 960 * @param value a `struct TEH_DenominationKey` 961 * @return #GNUNET_OK 962 */ 963 static enum GNUNET_GenericReturnValue 964 check_dk (void *cls, 965 const struct GNUNET_HashCode *hc, 966 void *value) 967 { 968 struct TEH_DenominationKey *dk = value; 969 970 (void) cls; 971 (void) hc; 972 switch (dk->denom_pub.bsign_pub_key->cipher) 973 { 974 case GNUNET_CRYPTO_BSA_INVALID: 975 break; 976 case GNUNET_CRYPTO_BSA_RSA: 977 GNUNET_assert (GNUNET_CRYPTO_rsa_public_key_check ( 978 dk->denom_pub.bsign_pub_key->details.rsa_public_key)); 979 return GNUNET_OK; 980 case GNUNET_CRYPTO_BSA_CS: 981 /* nothing to do for GNUNET_CRYPTO_BSA_CS */ 982 return GNUNET_OK; 983 } 984 GNUNET_assert (0); 985 return GNUNET_SYSERR; 986 } 987 988 989 void 990 TEH_check_invariants () 991 { 992 struct TEH_KeyStateHandle *ksh; 993 994 if (0 == TEH_check_invariants_flag) 995 return; 996 ksh = TEH_keys_get_state (); 997 if (NULL == ksh) 998 return; 999 GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map, 1000 &check_dk, 1001 NULL); 1002 } 1003 1004 1005 void 1006 TEH_resume_keys_requests (bool do_shutdown) 1007 { 1008 struct SuspendedKeysRequests *skr; 1009 1010 if (do_shutdown) 1011 terminating = true; 1012 while (NULL != (skr = skr_head)) 1013 { 1014 GNUNET_CONTAINER_DLL_remove (skr_head, 1015 skr_tail, 1016 skr); 1017 skr_size--; 1018 MHD_resume_connection (skr->connection); 1019 TALER_MHD_daemon_trigger (); 1020 GNUNET_free (skr); 1021 } 1022 GNUNET_assert (0 == skr_size); 1023 } 1024 1025 1026 /** 1027 * Clear memory for responses to "/keys" in @a ksh. 1028 * 1029 * @param[in,out] ksh key state to update 1030 */ 1031 static void 1032 clear_response_cache (struct TEH_KeyStateHandle *ksh) 1033 { 1034 for (unsigned int i = 0; i<ksh->krd_array_length; i++) 1035 { 1036 struct KeysResponseData *krd = &ksh->krd_array[i]; 1037 1038 MHD_destroy_response (krd->response_compressed); 1039 MHD_destroy_response (krd->response_uncompressed); 1040 GNUNET_free (krd->etag); 1041 } 1042 GNUNET_array_grow (ksh->krd_array, 1043 ksh->krd_array_length, 1044 0); 1045 } 1046 1047 1048 /** 1049 * Free denomination key data. 1050 * 1051 * @param cls a `struct TEH_KeyStateHandle`, unused 1052 * @param h_denom_pub hash of the denomination public key, unused 1053 * @param value a `struct TEH_DenominationKey` to free 1054 * @return #GNUNET_OK (continue to iterate) 1055 */ 1056 static enum GNUNET_GenericReturnValue 1057 clear_denomination_cb (void *cls, 1058 const struct GNUNET_HashCode *h_denom_pub, 1059 void *value) 1060 { 1061 struct TEH_DenominationKey *dk = value; 1062 struct TEH_AuditorSignature *as; 1063 1064 (void) cls; 1065 (void) h_denom_pub; 1066 TALER_denom_pub_free (&dk->denom_pub); 1067 while (NULL != (as = dk->as_head)) 1068 { 1069 GNUNET_CONTAINER_DLL_remove (dk->as_head, 1070 dk->as_tail, 1071 as); 1072 GNUNET_free (as); 1073 } 1074 GNUNET_free (dk); 1075 return GNUNET_OK; 1076 } 1077 1078 1079 /** 1080 * Free denomination key data. 1081 * 1082 * @param cls a `struct TEH_KeyStateHandle`, unused 1083 * @param pid the online signing key (type-disguised), unused 1084 * @param value a `struct SigningKey` to free 1085 * @return #GNUNET_OK (continue to iterate) 1086 */ 1087 static enum GNUNET_GenericReturnValue 1088 clear_signkey_cb (void *cls, 1089 const struct GNUNET_PeerIdentity *pid, 1090 void *value) 1091 { 1092 struct SigningKey *sk = value; 1093 1094 (void) cls; 1095 (void) pid; 1096 GNUNET_free (sk); 1097 return GNUNET_OK; 1098 } 1099 1100 1101 /** 1102 * Free resources associated with @a cls, possibly excluding 1103 * the helper data. 1104 * 1105 * @param[in] ksh key state to release 1106 */ 1107 static void 1108 destroy_key_state (struct TEH_KeyStateHandle *ksh) 1109 { 1110 struct TEH_GlobalFee *gf; 1111 1112 clear_response_cache (ksh); 1113 while (NULL != (gf = ksh->gf_head)) 1114 { 1115 GNUNET_CONTAINER_DLL_remove (ksh->gf_head, 1116 ksh->gf_tail, 1117 gf); 1118 GNUNET_free (gf); 1119 } 1120 GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map, 1121 &clear_denomination_cb, 1122 ksh); 1123 GNUNET_CONTAINER_multihashmap_destroy (ksh->denomkey_map); 1124 GNUNET_CONTAINER_multihashmap32_destroy (ksh->denomserial_map); 1125 GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map, 1126 &clear_signkey_cb, 1127 ksh); 1128 GNUNET_CONTAINER_multipeermap_destroy (ksh->signkey_map); 1129 json_decref (ksh->auditors); 1130 ksh->auditors = NULL; 1131 json_decref (ksh->global_fees); 1132 ksh->global_fees = NULL; 1133 if (NULL != ksh->management_keys_reply) 1134 { 1135 json_decref (ksh->management_keys_reply); 1136 ksh->management_keys_reply = NULL; 1137 } 1138 GNUNET_free (ksh); 1139 } 1140 1141 1142 /** 1143 * Function called whenever another exchange process has updated 1144 * the keys data in the database. 1145 * 1146 * @param cls NULL 1147 * @param extra unused 1148 * @param extra_size number of bytes in @a extra unused 1149 */ 1150 static void 1151 keys_update_event_cb (void *cls, 1152 const void *extra, 1153 size_t extra_size) 1154 { 1155 (void) cls; 1156 (void) extra; 1157 (void) extra_size; 1158 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1159 "Received /keys update event\n"); 1160 TEH_check_invariants (); 1161 key_generation++; 1162 TEH_resume_keys_requests (false); 1163 TEH_check_invariants (); 1164 } 1165 1166 1167 enum GNUNET_GenericReturnValue 1168 TEH_keys_init () 1169 { 1170 if (GNUNET_OK != 1171 TEH_SECMOD_setup_key_helpers ()) 1172 { 1173 TEH_SECMOD_destroy_key_helpers (); 1174 return GNUNET_SYSERR; 1175 } 1176 if (GNUNET_OK != 1177 GNUNET_CONFIGURATION_get_value_time (TEH_cfg, 1178 "exchange", 1179 "SIGNKEY_LEGAL_DURATION", 1180 &signkey_legal_duration)) 1181 { 1182 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1183 "exchange", 1184 "SIGNKEY_LEGAL_DURATION"); 1185 return GNUNET_SYSERR; 1186 } 1187 if (GNUNET_OK != 1188 GNUNET_CONFIGURATION_get_value_string (TEH_cfg, 1189 "exchange", 1190 "ASSET_TYPE", 1191 &asset_type)) 1192 { 1193 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, 1194 "exchange", 1195 "ASSET_TYPE"); 1196 asset_type = GNUNET_strdup ("fiat"); 1197 } 1198 { 1199 struct GNUNET_DB_EventHeaderP es = { 1200 .size = htons (sizeof (es)), 1201 .type = htons (TALER_DBEVENT_EXCHANGE_KEYS_UPDATED), 1202 }; 1203 1204 keys_eh = TALER_EXCHANGEDB_event_listen (TEH_pg, 1205 GNUNET_TIME_UNIT_FOREVER_REL, 1206 &es, 1207 &keys_update_event_cb, 1208 NULL); 1209 if (NULL == keys_eh) 1210 { 1211 GNUNET_break (0); 1212 return GNUNET_SYSERR; 1213 } 1214 } 1215 { 1216 struct GNUNET_DB_EventHeaderP es = { 1217 .size = htons (sizeof (es)), 1218 .type = htons (TALER_DBEVENT_EXCHANGE_KEYS_UPDATED), 1219 }; 1220 1221 wire_eh = TALER_EXCHANGEDB_event_listen (TEH_pg, 1222 GNUNET_TIME_UNIT_FOREVER_REL, 1223 &es, 1224 &wire_update_event_cb, 1225 NULL); 1226 if (NULL == wire_eh) 1227 { 1228 GNUNET_break (0); 1229 return GNUNET_SYSERR; 1230 } 1231 } 1232 return GNUNET_OK; 1233 } 1234 1235 1236 /** 1237 * Fully clean up our state. 1238 */ 1239 void 1240 TEH_keys_finished () 1241 { 1242 if (NULL != wire_state) 1243 { 1244 destroy_wire_state (wire_state); 1245 wire_state = NULL; 1246 } 1247 if (NULL != wire_eh) 1248 { 1249 TALER_EXCHANGEDB_event_listen_cancel (TEH_pg, 1250 wire_eh); 1251 wire_eh = NULL; 1252 } 1253 if (NULL != keys_tt) 1254 { 1255 GNUNET_SCHEDULER_cancel (keys_tt); 1256 keys_tt = NULL; 1257 } 1258 if (NULL != key_state) 1259 destroy_key_state (key_state); 1260 if (NULL != keys_eh) 1261 { 1262 TALER_EXCHANGEDB_event_listen_cancel (TEH_pg, 1263 keys_eh); 1264 keys_eh = NULL; 1265 } 1266 TEH_SECMOD_destroy_key_helpers (); 1267 } 1268 1269 1270 /** 1271 * Function called with information about the exchange's denomination keys. 1272 * 1273 * @param ksh closure with a `struct TEH_KeyStateHandle *` 1274 * @param denom_pub public key of the denomination 1275 * @param h_denom_pub hash of @a denom_pub 1276 * @param meta meta data information about the denomination type (value, expirations, fees) 1277 * @param master_sig master signature affirming the validity of this denomination 1278 * @param recoup_possible true if the key was revoked and clients can currently recoup 1279 * coins of this denomination 1280 */ 1281 static void 1282 denomination_info_cb ( 1283 struct TEH_KeyStateHandle *ksh, 1284 const struct TALER_DenominationPublicKey *denom_pub, 1285 const struct TALER_DenominationHashP *h_denom_pub, 1286 const struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta, 1287 const struct TALER_MasterSignatureP *master_sig, 1288 bool recoup_possible) 1289 { 1290 struct TEH_DenominationKey *dk; 1291 1292 if (GNUNET_TIME_absolute_is_past (meta->expire_deposit.abs_time)) 1293 { 1294 /* should have been filtered by DB query already! */ 1295 GNUNET_break (0); 1296 return; 1297 } 1298 if (GNUNET_OK != 1299 TALER_exchange_offline_denom_validity_verify ( 1300 h_denom_pub, 1301 meta->start, 1302 meta->expire_withdraw, 1303 meta->expire_deposit, 1304 meta->expire_legal, 1305 &meta->value, 1306 &meta->fees, 1307 &TEH_master_public_key, 1308 master_sig)) 1309 { 1310 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1311 "Database has denomination with invalid signature. Skipping entry. Did the exchange offline public key change?\n"); 1312 return; 1313 } 1314 1315 GNUNET_assert (GNUNET_CRYPTO_BSA_INVALID != 1316 denom_pub->bsign_pub_key->cipher); 1317 if (GNUNET_TIME_absolute_is_zero (meta->start.abs_time) || 1318 GNUNET_TIME_absolute_is_zero (meta->expire_withdraw.abs_time) || 1319 GNUNET_TIME_absolute_is_zero (meta->expire_deposit.abs_time) || 1320 GNUNET_TIME_absolute_is_zero (meta->expire_legal.abs_time) ) 1321 { 1322 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1323 "Database contains invalid denomination key %s\n", 1324 GNUNET_h2s (&h_denom_pub->hash)); 1325 return; 1326 } 1327 dk = GNUNET_new (struct TEH_DenominationKey); 1328 TALER_denom_pub_copy (&dk->denom_pub, 1329 denom_pub); 1330 dk->h_denom_pub = *h_denom_pub; 1331 dk->meta = *meta; 1332 dk->master_sig = *master_sig; 1333 dk->recoup_possible = recoup_possible; 1334 dk->denom_pub.age_mask = meta->age_mask; 1335 1336 GNUNET_assert ( 1337 GNUNET_OK == 1338 GNUNET_CONTAINER_multihashmap_put (ksh->denomkey_map, 1339 &dk->h_denom_pub.hash, 1340 dk, 1341 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); 1342 { 1343 uint32_t serial32 = (uint32_t) dk->meta.serial; 1344 1345 GNUNET_assert (dk->meta.serial == (uint64_t) serial32); 1346 GNUNET_assert ( 1347 GNUNET_OK == 1348 GNUNET_CONTAINER_multihashmap32_put (ksh->denomserial_map, 1349 serial32, 1350 dk, 1351 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); 1352 } 1353 } 1354 1355 1356 /** 1357 * Function called with information about the exchange's online signing keys. 1358 * 1359 * @param ksh closure with a `struct TEH_KeyStateHandle *` 1360 * @param exchange_pub the public key 1361 * @param meta meta data information about the denomination type (expirations) 1362 * @param master_sig master signature affirming the validity of this denomination 1363 */ 1364 static void 1365 signkey_info_cb ( 1366 struct TEH_KeyStateHandle *ksh, 1367 const struct TALER_ExchangePublicKeyP *exchange_pub, 1368 const struct TALER_EXCHANGEDB_SignkeyMetaData *meta, 1369 const struct TALER_MasterSignatureP *master_sig) 1370 { 1371 struct SigningKey *sk; 1372 struct GNUNET_PeerIdentity pid; 1373 1374 if (GNUNET_OK != 1375 TALER_exchange_offline_signkey_validity_verify ( 1376 exchange_pub, 1377 meta->start, 1378 meta->expire_sign, 1379 meta->expire_legal, 1380 &TEH_master_public_key, 1381 master_sig)) 1382 { 1383 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1384 "Database has signing key with invalid signature. Skipping entry. Did the exchange offline public key change?\n"); 1385 return; 1386 } 1387 sk = GNUNET_new (struct SigningKey); 1388 sk->exchange_pub = *exchange_pub; 1389 sk->meta = *meta; 1390 sk->master_sig = *master_sig; 1391 pid.public_key = exchange_pub->eddsa_pub; 1392 GNUNET_assert ( 1393 GNUNET_OK == 1394 GNUNET_CONTAINER_multipeermap_put (ksh->signkey_map, 1395 &pid, 1396 sk, 1397 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); 1398 } 1399 1400 1401 /** 1402 * Closure for #get_auditor_sigs. 1403 */ 1404 struct GetAuditorSigsContext 1405 { 1406 /** 1407 * Where to store the matching signatures. 1408 */ 1409 json_t *denom_keys; 1410 1411 /** 1412 * Public key of the auditor to match against. 1413 */ 1414 const struct TALER_AuditorPublicKeyP *auditor_pub; 1415 }; 1416 1417 1418 /** 1419 * Extract the auditor signatures matching the auditor's public 1420 * key from the @a value and generate the respective JSON. 1421 * 1422 * @param cls a `struct GetAuditorSigsContext` 1423 * @param h_denom_pub hash of the denomination public key 1424 * @param value a `struct TEH_DenominationKey` 1425 * @return #GNUNET_OK (continue to iterate) 1426 */ 1427 static enum GNUNET_GenericReturnValue 1428 get_auditor_sigs (void *cls, 1429 const struct GNUNET_HashCode *h_denom_pub, 1430 void *value) 1431 { 1432 struct GetAuditorSigsContext *ctx = cls; 1433 struct TEH_DenominationKey *dk = value; 1434 1435 for (struct TEH_AuditorSignature *as = dk->as_head; 1436 NULL != as; 1437 as = as->next) 1438 { 1439 if (0 != 1440 GNUNET_memcmp (ctx->auditor_pub, 1441 &as->apub)) 1442 continue; 1443 GNUNET_break (0 == 1444 json_array_append_new ( 1445 ctx->denom_keys, 1446 GNUNET_JSON_PACK ( 1447 GNUNET_JSON_pack_data_auto ("denom_pub_h", 1448 h_denom_pub), 1449 GNUNET_JSON_pack_data_auto ("auditor_sig", 1450 &as->asig)))); 1451 } 1452 return GNUNET_OK; 1453 } 1454 1455 1456 /** 1457 * Function called with information about the exchange's auditors. 1458 * 1459 * @param ksh closure with a `struct TEH_KeyStateHandle *` 1460 * @param auditor_pub the public key of the auditor 1461 * @param auditor_url URL of the REST API of the auditor 1462 * @param auditor_name human readable official name of the auditor 1463 */ 1464 static void 1465 auditor_info_cb ( 1466 struct TEH_KeyStateHandle *ksh, 1467 const struct TALER_AuditorPublicKeyP *auditor_pub, 1468 const char *auditor_url, 1469 const char *auditor_name) 1470 { 1471 struct GetAuditorSigsContext ctx; 1472 1473 ctx.denom_keys = json_array (); 1474 GNUNET_assert (NULL != ctx.denom_keys); 1475 ctx.auditor_pub = auditor_pub; 1476 GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map, 1477 &get_auditor_sigs, 1478 &ctx); 1479 GNUNET_break (0 == 1480 json_array_append_new ( 1481 ksh->auditors, 1482 GNUNET_JSON_PACK ( 1483 GNUNET_JSON_pack_string ("auditor_name", 1484 auditor_name), 1485 GNUNET_JSON_pack_data_auto ("auditor_pub", 1486 auditor_pub), 1487 GNUNET_JSON_pack_string ("auditor_url", 1488 auditor_url), 1489 GNUNET_JSON_pack_array_steal ("denomination_keys", 1490 ctx.denom_keys)))); 1491 } 1492 1493 1494 /** 1495 * Function called with information about the denominations 1496 * audited by the exchange's auditors. 1497 * 1498 * @param ksh closure with a `struct TEH_KeyStateHandle *` 1499 * @param auditor_pub the public key of an auditor 1500 * @param h_denom_pub hash of a denomination key audited by this auditor 1501 * @param auditor_sig signature from the auditor affirming this 1502 */ 1503 static void 1504 auditor_denom_cb ( 1505 struct TEH_KeyStateHandle *ksh, 1506 const struct TALER_AuditorPublicKeyP *auditor_pub, 1507 const struct TALER_DenominationHashP *h_denom_pub, 1508 const struct TALER_AuditorSignatureP *auditor_sig) 1509 { 1510 struct TEH_DenominationKey *dk; 1511 struct TEH_AuditorSignature *as; 1512 1513 dk = GNUNET_CONTAINER_multihashmap_get (ksh->denomkey_map, 1514 &h_denom_pub->hash); 1515 if (NULL == dk) 1516 { 1517 /* Odd, this should be impossible as per foreign key 1518 constraint on 'auditor_denom_sigs'! Well, we can 1519 safely continue anyway, so let's just log it. */ 1520 GNUNET_break (0); 1521 return; 1522 } 1523 as = GNUNET_new (struct TEH_AuditorSignature); 1524 as->asig = *auditor_sig; 1525 as->apub = *auditor_pub; 1526 GNUNET_CONTAINER_DLL_insert (dk->as_head, 1527 dk->as_tail, 1528 as); 1529 } 1530 1531 1532 /** 1533 * Closure for #add_sign_key_cb. 1534 */ 1535 struct SignKeyCtx 1536 { 1537 /** 1538 * What is the current rotation frequency for signing keys. Updated. 1539 */ 1540 struct GNUNET_TIME_Relative min_sk_frequency; 1541 1542 /** 1543 * JSON array of signing keys (being created). 1544 */ 1545 json_t *signkeys; 1546 }; 1547 1548 1549 /** 1550 * Function called for all signing keys, used to build up the 1551 * respective JSON response. 1552 * 1553 * @param cls a `struct SignKeyCtx *` with the array to append keys to 1554 * @param pid the exchange public key (in type disguise) 1555 * @param value a `struct SigningKey` 1556 * @return #GNUNET_OK (continue to iterate) 1557 */ 1558 static enum GNUNET_GenericReturnValue 1559 add_sign_key_cb (void *cls, 1560 const struct GNUNET_PeerIdentity *pid, 1561 void *value) 1562 { 1563 struct SignKeyCtx *ctx = cls; 1564 struct SigningKey *sk = value; 1565 1566 (void) pid; 1567 if (GNUNET_TIME_absolute_is_future (sk->meta.expire_sign.abs_time)) 1568 { 1569 ctx->min_sk_frequency = 1570 GNUNET_TIME_relative_min (ctx->min_sk_frequency, 1571 GNUNET_TIME_absolute_get_difference ( 1572 sk->meta.start.abs_time, 1573 sk->meta.expire_sign.abs_time)); 1574 } 1575 GNUNET_assert ( 1576 0 == 1577 json_array_append_new ( 1578 ctx->signkeys, 1579 GNUNET_JSON_PACK ( 1580 GNUNET_JSON_pack_timestamp ("stamp_start", 1581 sk->meta.start), 1582 GNUNET_JSON_pack_timestamp ("stamp_expire", 1583 sk->meta.expire_sign), 1584 GNUNET_JSON_pack_timestamp ("stamp_end", 1585 sk->meta.expire_legal), 1586 GNUNET_JSON_pack_data_auto ("master_sig", 1587 &sk->master_sig), 1588 GNUNET_JSON_pack_data_auto ("key", 1589 &sk->exchange_pub)))); 1590 return GNUNET_OK; 1591 } 1592 1593 1594 /** 1595 * Closure for #add_denom_key_cb. 1596 */ 1597 struct DenomKeyCtx 1598 { 1599 /** 1600 * Heap for sorting active denomination keys by start time. 1601 */ 1602 struct GNUNET_CONTAINER_Heap *heap; 1603 1604 /** 1605 * JSON array of revoked denomination keys. 1606 */ 1607 json_t *recoup; 1608 1609 /** 1610 * What is the minimum key rotation frequency of 1611 * valid denomination keys? 1612 */ 1613 struct GNUNET_TIME_Relative min_dk_frequency; 1614 }; 1615 1616 1617 /** 1618 * Function called for all denomination keys, used to build up the 1619 * JSON list of *revoked* denomination keys and the 1620 * heap of non-revoked denomination keys by timeout. 1621 * 1622 * @param cls a `struct DenomKeyCtx` 1623 * @param h_denom_pub hash of the denomination key 1624 * @param value a `struct TEH_DenominationKey` 1625 * @return #GNUNET_OK (continue to iterate) 1626 */ 1627 static enum GNUNET_GenericReturnValue 1628 add_denom_key_cb (void *cls, 1629 const struct GNUNET_HashCode *h_denom_pub, 1630 void *value) 1631 { 1632 struct DenomKeyCtx *dkc = cls; 1633 struct TEH_DenominationKey *dk = value; 1634 1635 if (dk->recoup_possible) 1636 { 1637 GNUNET_assert ( 1638 0 == 1639 json_array_append_new ( 1640 dkc->recoup, 1641 GNUNET_JSON_PACK ( 1642 GNUNET_JSON_pack_data_auto ("h_denom_pub", 1643 h_denom_pub)))); 1644 } 1645 else 1646 { 1647 if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) 1648 { 1649 dkc->min_dk_frequency = 1650 GNUNET_TIME_relative_min (dkc->min_dk_frequency, 1651 GNUNET_TIME_absolute_get_difference ( 1652 dk->meta.start.abs_time, 1653 dk->meta.expire_withdraw.abs_time)); 1654 } 1655 (void) GNUNET_CONTAINER_heap_insert (dkc->heap, 1656 dk, 1657 dk->meta.start.abs_time.abs_value_us); 1658 } 1659 return GNUNET_OK; 1660 } 1661 1662 1663 /** 1664 * Add the headers we want to set for every /keys response. 1665 * 1666 * @param cls the key state to use 1667 * @param[in,out] response the response to modify 1668 */ 1669 static void 1670 setup_general_response_headers (void *cls, 1671 struct MHD_Response *response) 1672 { 1673 struct TEH_KeyStateHandle *ksh = cls; 1674 char dat[128]; 1675 1676 TALER_MHD_add_global_headers (response, 1677 true); 1678 GNUNET_break (MHD_YES == 1679 MHD_add_response_header (response, 1680 MHD_HTTP_HEADER_CONTENT_TYPE, 1681 "application/json")); 1682 GNUNET_break (MHD_YES == 1683 MHD_add_response_header (response, 1684 MHD_HTTP_HEADER_CACHE_CONTROL, 1685 "public,must-revalidate,max-age=86400") 1686 ); 1687 if (! GNUNET_TIME_relative_is_zero (ksh->rekey_frequency)) 1688 { 1689 struct GNUNET_TIME_Relative r; 1690 struct GNUNET_TIME_Absolute a; 1691 struct GNUNET_TIME_Timestamp km; 1692 struct GNUNET_TIME_Timestamp m; 1693 struct GNUNET_TIME_Timestamp we; 1694 1695 r = GNUNET_TIME_relative_min (TEH_max_keys_caching, 1696 ksh->rekey_frequency); 1697 a = GNUNET_TIME_relative_to_absolute (r); 1698 /* Round up to next full day to ensure the expiration 1699 time does not become a fingerprint! */ 1700 a = GNUNET_TIME_absolute_round_down (a, 1701 GNUNET_TIME_UNIT_DAYS); 1702 a = GNUNET_TIME_absolute_add (a, 1703 GNUNET_TIME_UNIT_DAYS); 1704 km = GNUNET_TIME_absolute_to_timestamp (a); 1705 we = GNUNET_TIME_absolute_to_timestamp (wire_state->cache_expiration); 1706 m = GNUNET_TIME_timestamp_min (we, 1707 km); 1708 TALER_MHD_get_date_string (m.abs_time, 1709 dat); 1710 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1711 "Setting /keys 'Expires' header to '%s' (rekey frequency is %s)\n", 1712 dat, 1713 GNUNET_TIME_relative2s (ksh->rekey_frequency, 1714 false)); 1715 GNUNET_break (MHD_YES == 1716 MHD_add_response_header (response, 1717 MHD_HTTP_HEADER_EXPIRES, 1718 dat)); 1719 ksh->signature_expires 1720 = GNUNET_TIME_timestamp_min (m, 1721 ksh->signature_expires); 1722 } 1723 /* Set cache control headers: our response varies depending on these headers */ 1724 GNUNET_break (MHD_YES == 1725 MHD_add_response_header (response, 1726 MHD_HTTP_HEADER_VARY, 1727 MHD_HTTP_HEADER_ACCEPT_ENCODING)); 1728 } 1729 1730 1731 /** 1732 * Initialize @a krd using the given values for @a signkeys, 1733 * @a recoup and @a denoms. 1734 * 1735 * @param[in,out] ksh key state handle we build @a krd for 1736 * @param[in] denom_keys_hash hash over all the denomination keys in @a denoms 1737 * @param last_cherry_pick_date timestamp to use 1738 * @param[in,out] signkeys list of sign keys to return 1739 * @param[in,out] recoup list of revoked keys to return 1740 * @param[in,out] grouped_denominations list of grouped denominations to return 1741 * @return #GNUNET_OK on success 1742 */ 1743 static enum GNUNET_GenericReturnValue 1744 create_krd (struct TEH_KeyStateHandle *ksh, 1745 const struct GNUNET_HashCode *denom_keys_hash, 1746 struct GNUNET_TIME_Timestamp last_cherry_pick_date, 1747 json_t *signkeys, 1748 json_t *recoup, 1749 json_t *grouped_denominations) 1750 { 1751 struct KeysResponseData krd; 1752 struct TALER_ExchangePublicKeyP exchange_pub; 1753 struct TALER_ExchangeSignatureP exchange_sig; 1754 struct WireStateHandle *wsh; 1755 json_t *keys; 1756 1757 wsh = get_wire_state (); 1758 if (! wsh->ready) 1759 { 1760 GNUNET_break (0); 1761 return GNUNET_SYSERR; 1762 } 1763 GNUNET_assert (! GNUNET_TIME_absolute_is_zero ( 1764 last_cherry_pick_date.abs_time)); 1765 GNUNET_assert (NULL != signkeys); 1766 GNUNET_assert (NULL != recoup); 1767 GNUNET_assert (NULL != grouped_denominations); 1768 GNUNET_assert (NULL != ksh->auditors); 1769 GNUNET_assert (NULL != TEH_currency); 1770 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1771 "Creating /keys at cherry pick date %s\n", 1772 GNUNET_TIME_timestamp2s (last_cherry_pick_date)); 1773 1774 /* Sign hash over master signatures of all denomination keys until this time 1775 (in reverse order). */ 1776 { 1777 enum TALER_ErrorCode ec; 1778 1779 if (TALER_EC_NONE != 1780 (ec = 1781 TALER_exchange_online_key_set_sign ( 1782 &TEH_keys_exchange_sign2_, 1783 ksh, 1784 last_cherry_pick_date, 1785 denom_keys_hash, 1786 &exchange_pub, 1787 &exchange_sig))) 1788 { 1789 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1790 "Could not create key response data: cannot sign (%s)\n", 1791 TALER_ErrorCode_get_hint (ec)); 1792 return GNUNET_SYSERR; 1793 } 1794 } 1795 1796 { 1797 const struct SigningKey *sk; 1798 1799 sk = GNUNET_CONTAINER_multipeermap_get ( 1800 ksh->signkey_map, 1801 (const struct GNUNET_PeerIdentity *) &exchange_pub); 1802 GNUNET_assert (NULL != sk); 1803 ksh->signature_expires = GNUNET_TIME_timestamp_min (sk->meta.expire_sign, 1804 ksh->signature_expires); 1805 } 1806 1807 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1808 "Build /keys data with %u wire accounts\n", 1809 (unsigned int) json_array_size ( 1810 json_object_get (wsh->json_reply, 1811 "accounts"))); 1812 1813 keys = GNUNET_JSON_PACK ( 1814 GNUNET_JSON_pack_string ("version", 1815 EXCHANGE_PROTOCOL_VERSION), 1816 GNUNET_JSON_pack_string ("base_url", 1817 TEH_base_url), 1818 GNUNET_JSON_pack_string ("currency", 1819 TEH_currency), 1820 GNUNET_JSON_pack_allow_null ( 1821 GNUNET_JSON_pack_string ( 1822 "bank_compliance_language", 1823 TEH_bank_compliance_language)), 1824 GNUNET_JSON_pack_object_steal ( 1825 "currency_specification", 1826 TALER_JSON_currency_specs_to_json (TEH_cspec)), 1827 GNUNET_JSON_pack_array_incref ( 1828 "hard_limits", 1829 TEH_hard_limits), 1830 GNUNET_JSON_pack_array_incref ( 1831 "zero_limits", 1832 TEH_zero_limits), 1833 TALER_JSON_pack_amount ("stefan_abs", 1834 &TEH_stefan_abs), 1835 TALER_JSON_pack_amount ("stefan_log", 1836 &TEH_stefan_log), 1837 GNUNET_JSON_pack_double ("stefan_lin", 1838 (double) TEH_stefan_lin), 1839 GNUNET_JSON_pack_string ("asset_type", 1840 asset_type), 1841 GNUNET_JSON_pack_bool ("kyc_enabled", 1842 GNUNET_YES == TEH_enable_kyc), 1843 GNUNET_JSON_pack_bool ("kyc_swap_tos_acceptance", 1844 GNUNET_YES == TEH_kyc_swap_tos_acceptance), 1845 GNUNET_JSON_pack_bool ("disable_direct_deposit", 1846 GNUNET_YES == TEH_disable_direct_deposit), 1847 GNUNET_JSON_pack_bool ("rewards_allowed", 1848 false), 1849 GNUNET_JSON_pack_data_auto ("master_public_key", 1850 &TEH_master_public_key), 1851 GNUNET_JSON_pack_time_rel ("reserve_closing_delay", 1852 TEH_reserve_closing_delay), 1853 GNUNET_JSON_pack_time_rel ("default_p2p_push_expiration", 1854 TEH_default_p2p_expiration), 1855 GNUNET_JSON_pack_array_incref ("signkeys", 1856 signkeys), 1857 GNUNET_JSON_pack_array_incref ("recoup", 1858 recoup), 1859 GNUNET_JSON_pack_array_incref ("wads", 1860 json_object_get (wsh->json_reply, 1861 "wads")), 1862 GNUNET_JSON_pack_array_incref ("accounts", 1863 json_object_get (wsh->json_reply, 1864 "accounts")), 1865 GNUNET_JSON_pack_object_incref ("wire_fees", 1866 json_object_get (wsh->json_reply, 1867 "fees")), 1868 GNUNET_JSON_pack_array_incref ("denominations", 1869 grouped_denominations), 1870 GNUNET_JSON_pack_array_incref ("auditors", 1871 ksh->auditors), 1872 GNUNET_JSON_pack_array_incref ("global_fees", 1873 ksh->global_fees), 1874 GNUNET_JSON_pack_timestamp ("list_issue_date", 1875 last_cherry_pick_date), 1876 GNUNET_JSON_pack_allow_null ( 1877 GNUNET_JSON_pack_array_steal ( 1878 "wallet_balance_limit_without_kyc", 1879 TALER_KYCLOGIC_get_wallet_thresholds ())), 1880 GNUNET_JSON_pack_allow_null ( 1881 GNUNET_JSON_pack_string ("shopping_url", 1882 TEH_shopping_url)), 1883 TALER_JSON_pack_amount ("tiny_amount", 1884 &TEH_tiny_amount), 1885 GNUNET_JSON_pack_data_auto ("exchange_pub", 1886 &exchange_pub), 1887 GNUNET_JSON_pack_data_auto ("exchange_sig", 1888 &exchange_sig)); 1889 GNUNET_assert (NULL != keys); 1890 1891 { 1892 char *keys_json; 1893 void *keys_jsonz; 1894 size_t keys_jsonz_size; 1895 int comp; 1896 char etag[sizeof (struct GNUNET_HashCode) * 2]; 1897 1898 /* Convert /keys response to UTF8-String */ 1899 keys_json = json_dumps (keys, 1900 JSON_INDENT (2)); 1901 json_decref (keys); 1902 GNUNET_assert (NULL != keys_json); 1903 1904 /* Keep copy for later compression... */ 1905 keys_jsonz = GNUNET_strdup (keys_json); 1906 keys_jsonz_size = strlen (keys_json); 1907 1908 /* hash to compute etag */ 1909 { 1910 struct GNUNET_HashCode ehash; 1911 char *end; 1912 1913 GNUNET_CRYPTO_hash (keys_jsonz, 1914 keys_jsonz_size, 1915 &ehash); 1916 end = GNUNET_STRINGS_data_to_string (&ehash, 1917 sizeof (ehash), 1918 etag, 1919 sizeof (etag)); 1920 *end = '\0'; 1921 } 1922 1923 /* Create uncompressed response */ 1924 krd.response_uncompressed 1925 = MHD_create_response_from_buffer (keys_jsonz_size, 1926 keys_json, 1927 MHD_RESPMEM_MUST_FREE); 1928 GNUNET_assert (NULL != krd.response_uncompressed); 1929 setup_general_response_headers (ksh, 1930 krd.response_uncompressed); 1931 /* Information is always public, revalidate after 1 day */ 1932 GNUNET_break (MHD_YES == 1933 MHD_add_response_header (krd.response_uncompressed, 1934 MHD_HTTP_HEADER_ETAG, 1935 etag)); 1936 /* Also compute compressed version of /keys response */ 1937 comp = TALER_MHD_body_compress (&keys_jsonz, 1938 &keys_jsonz_size); 1939 krd.response_compressed 1940 = MHD_create_response_from_buffer (keys_jsonz_size, 1941 keys_jsonz, 1942 MHD_RESPMEM_MUST_FREE); 1943 GNUNET_assert (NULL != krd.response_compressed); 1944 /* If the response is actually compressed, set the 1945 respective header. */ 1946 GNUNET_assert ( (MHD_YES != comp) || 1947 (MHD_YES == 1948 MHD_add_response_header (krd.response_compressed, 1949 MHD_HTTP_HEADER_CONTENT_ENCODING, 1950 "deflate")) ); 1951 setup_general_response_headers (ksh, 1952 krd.response_compressed); 1953 /* Information is always public, revalidate after 1 day */ 1954 GNUNET_break (MHD_YES == 1955 MHD_add_response_header (krd.response_compressed, 1956 MHD_HTTP_HEADER_ETAG, 1957 etag)); 1958 krd.etag = GNUNET_strdup (etag); 1959 } 1960 krd.cherry_pick_date = last_cherry_pick_date; 1961 GNUNET_array_append (ksh->krd_array, 1962 ksh->krd_array_length, 1963 krd); 1964 return GNUNET_OK; 1965 } 1966 1967 1968 /** 1969 * Element in the `struct SignatureContext` array. 1970 */ 1971 struct SignatureElement 1972 { 1973 1974 /** 1975 * Offset of the denomination in the group array, 1976 * for sorting (2nd rank, ascending). 1977 */ 1978 unsigned int offset; 1979 1980 /** 1981 * Offset of the group in the denominations array, 1982 * for sorting (2nd rank, ascending). 1983 */ 1984 unsigned int group_offset; 1985 1986 /** 1987 * Pointer to actual master signature to hash over. 1988 */ 1989 struct TALER_MasterSignatureP master_sig; 1990 }; 1991 1992 /** 1993 * Context for collecting the array of master signatures 1994 * needed to verify the exchange_sig online signature. 1995 */ 1996 struct SignatureContext 1997 { 1998 /** 1999 * Array of signatures to hash over. 2000 */ 2001 struct SignatureElement *elements; 2002 2003 /** 2004 * Write offset in the @e elements array. 2005 */ 2006 unsigned int elements_pos; 2007 2008 /** 2009 * Allocated space for @e elements. 2010 */ 2011 unsigned int elements_size; 2012 }; 2013 2014 2015 /** 2016 * Determine order to sort two elements by before 2017 * we hash the master signatures. Used for 2018 * sorting with qsort(). 2019 * 2020 * @param a pointer to a `struct SignatureElement` 2021 * @param b pointer to a `struct SignatureElement` 2022 * @return 0 if equal, -1 if a < b, 1 if a > b. 2023 */ 2024 static int 2025 signature_context_sort_cb (const void *a, 2026 const void *b) 2027 { 2028 const struct SignatureElement *sa = a; 2029 const struct SignatureElement *sb = b; 2030 2031 if (sa->group_offset < sb->group_offset) 2032 return -1; 2033 if (sa->group_offset > sb->group_offset) 2034 return 1; 2035 if (sa->offset < sb->offset) 2036 return -1; 2037 if (sa->offset > sb->offset) 2038 return 1; 2039 /* We should never have two disjoint elements 2040 with same time and offset */ 2041 GNUNET_assert (sa == sb); 2042 return 0; 2043 } 2044 2045 2046 /** 2047 * Append a @a master_sig to the @a sig_ctx using the 2048 * given attributes for (later) sorting. 2049 * 2050 * @param[in,out] sig_ctx signature context to update 2051 * @param group_offset offset for the group 2052 * @param offset offset for the entry 2053 * @param master_sig master signature for the entry 2054 */ 2055 static void 2056 append_signature (struct SignatureContext *sig_ctx, 2057 unsigned int group_offset, 2058 unsigned int offset, 2059 const struct TALER_MasterSignatureP *master_sig) 2060 { 2061 struct SignatureElement *element; 2062 unsigned int new_size; 2063 2064 if (sig_ctx->elements_pos == sig_ctx->elements_size) 2065 { 2066 if (0 == sig_ctx->elements_size) 2067 new_size = 1024; 2068 else 2069 new_size = sig_ctx->elements_size * 2; 2070 GNUNET_array_grow (sig_ctx->elements, 2071 sig_ctx->elements_size, 2072 new_size); 2073 } 2074 element = &sig_ctx->elements[sig_ctx->elements_pos++]; 2075 element->offset = offset; 2076 element->group_offset = group_offset; 2077 element->master_sig = *master_sig; 2078 } 2079 2080 2081 /** 2082 * GroupData is the value we store for each group meta-data 2083 */ 2084 struct GroupData 2085 { 2086 /** 2087 * The json blob with the group meta-data and list of denominations 2088 */ 2089 json_t *json; 2090 2091 /** 2092 * List of denominations for the group, 2093 * included in @e json, do not free separately! 2094 */ 2095 json_t *list; 2096 2097 /** 2098 * Offset of the group in the final array. 2099 */ 2100 unsigned int group_off; 2101 2102 }; 2103 2104 2105 /** 2106 * Helper function called to clean up the group data 2107 * in the denominations_by_group below. 2108 * 2109 * @param cls unused 2110 * @param key unused 2111 * @param value a `struct GroupData` to free 2112 * @return #GNUNET_OK 2113 */ 2114 static int 2115 free_group (void *cls, 2116 const struct GNUNET_HashCode *key, 2117 void *value) 2118 { 2119 struct GroupData *gd = value; 2120 2121 (void) cls; 2122 (void) key; 2123 GNUNET_free (gd); 2124 return GNUNET_OK; 2125 } 2126 2127 2128 static void 2129 compute_msig_hash (struct SignatureContext *sig_ctx, 2130 struct GNUNET_HashCode *hc) 2131 { 2132 struct GNUNET_HashContext *hash_context; 2133 2134 hash_context = GNUNET_CRYPTO_hash_context_start (); 2135 qsort (sig_ctx->elements, 2136 sig_ctx->elements_pos, 2137 sizeof (struct SignatureElement), 2138 &signature_context_sort_cb); 2139 for (unsigned int i = 0; i<sig_ctx->elements_pos; i++) 2140 { 2141 struct SignatureElement *element = &sig_ctx->elements[i]; 2142 2143 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2144 "Adding %u,%u,%s\n", 2145 element->group_offset, 2146 element->offset, 2147 TALER_B2S (&element->master_sig)); 2148 GNUNET_CRYPTO_hash_context_read (hash_context, 2149 &element->master_sig, 2150 sizeof (element->master_sig)); 2151 } 2152 GNUNET_CRYPTO_hash_context_finish (hash_context, 2153 hc); 2154 } 2155 2156 2157 /** 2158 * Update the "/keys" responses in @a ksh, computing the detailed replies. 2159 * 2160 * This function is to recompute all (including cherry-picked) responses we 2161 * might want to return, based on the state already in @a ksh. 2162 * 2163 * @param[in,out] ksh state handle to update 2164 * @return #GNUNET_OK on success 2165 */ 2166 static enum GNUNET_GenericReturnValue 2167 finish_keys_response (struct TEH_KeyStateHandle *ksh) 2168 { 2169 enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR; 2170 json_t *recoup; 2171 struct SignKeyCtx sctx = { 2172 .min_sk_frequency = GNUNET_TIME_UNIT_FOREVER_REL 2173 }; 2174 json_t *grouped_denominations = NULL; 2175 struct GNUNET_TIME_Timestamp last_cherry_pick_date; 2176 struct GNUNET_CONTAINER_Heap *heap; 2177 struct SignatureContext sig_ctx = { 0 }; 2178 /* Remember if we have any denomination with age restriction */ 2179 bool has_age_restricted_denomination = false; 2180 struct WireStateHandle *wsh; 2181 2182 wsh = get_wire_state (); 2183 if (! wsh->ready) 2184 { 2185 GNUNET_break (0); 2186 return GNUNET_SYSERR; 2187 } 2188 if (0 == 2189 json_array_size (json_object_get (wsh->json_reply, 2190 "accounts")) ) 2191 { 2192 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2193 "No wire accounts available. Refusing to generate /keys response.\n"); 2194 return GNUNET_NO; 2195 } 2196 sctx.signkeys = json_array (); 2197 GNUNET_assert (NULL != sctx.signkeys); 2198 recoup = json_array (); 2199 GNUNET_assert (NULL != recoup); 2200 grouped_denominations = json_array (); 2201 GNUNET_assert (NULL != grouped_denominations); 2202 2203 GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map, 2204 &add_sign_key_cb, 2205 &sctx); 2206 if (0 == json_array_size (sctx.signkeys)) 2207 { 2208 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2209 "No online signing keys available. Refusing to generate /keys response.\n"); 2210 ret = GNUNET_NO; 2211 goto CLEANUP; 2212 } 2213 heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX); 2214 { 2215 struct DenomKeyCtx dkc = { 2216 .recoup = recoup, 2217 .heap = heap, 2218 .min_dk_frequency = GNUNET_TIME_UNIT_FOREVER_REL, 2219 }; 2220 2221 GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map, 2222 &add_denom_key_cb, 2223 &dkc); 2224 ksh->rekey_frequency 2225 = GNUNET_TIME_relative_min (dkc.min_dk_frequency, 2226 sctx.min_sk_frequency); 2227 } 2228 2229 last_cherry_pick_date = GNUNET_TIME_UNIT_ZERO_TS; 2230 2231 { 2232 struct TEH_DenominationKey *dk; 2233 struct GNUNET_CONTAINER_MultiHashMap *denominations_by_group; 2234 2235 denominations_by_group = 2236 GNUNET_CONTAINER_multihashmap_create (1024, 2237 GNUNET_NO /* NO, because keys are only on the stack */ 2238 ); 2239 /* heap = max heap, sorted by start time */ 2240 while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap))) 2241 { 2242 if (GNUNET_TIME_timestamp_cmp (last_cherry_pick_date, 2243 !=, 2244 dk->meta.start) && 2245 (! GNUNET_TIME_absolute_is_zero (last_cherry_pick_date.abs_time)) ) 2246 { 2247 /* 2248 * This is not the first entry in the heap (because last_cherry_pick_date != 2249 * GNUNET_TIME_UNIT_ZERO_TS) and the previous entry had a different 2250 * start time. Therefore, we create a new entry in ksh. 2251 */ 2252 struct GNUNET_HashCode hc; 2253 2254 compute_msig_hash (&sig_ctx, 2255 &hc); 2256 if (GNUNET_OK != 2257 create_krd (ksh, 2258 &hc, 2259 last_cherry_pick_date, 2260 sctx.signkeys, 2261 recoup, 2262 grouped_denominations)) 2263 { 2264 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2265 "Failed to generate key response data for %s\n", 2266 GNUNET_TIME_timestamp2s (last_cherry_pick_date)); 2267 /* drain heap before destroying it */ 2268 while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap))) 2269 /* intentionally empty */; 2270 GNUNET_CONTAINER_heap_destroy (heap); 2271 goto CLEANUP; 2272 } 2273 } 2274 2275 last_cherry_pick_date = dk->meta.start; 2276 /* 2277 * Group the denominations by {cipher, value, fees, age_mask}. 2278 * 2279 * For each group we save the group meta-data and the list of 2280 * denominations in this group as a json-blob in the multihashmap 2281 * denominations_by_group. 2282 */ 2283 { 2284 struct GroupData *group; 2285 json_t *entry; 2286 struct GNUNET_HashCode key; 2287 struct TALER_DenominationGroup meta = { 2288 .cipher = dk->denom_pub.bsign_pub_key->cipher, 2289 .value = dk->meta.value, 2290 .fees = dk->meta.fees, 2291 .age_mask = dk->meta.age_mask, 2292 }; 2293 2294 /* Search the group/JSON-blob for the key */ 2295 TALER_denomination_group_get_key (&meta, 2296 &key); 2297 group = GNUNET_CONTAINER_multihashmap_get ( 2298 denominations_by_group, 2299 &key); 2300 if (NULL == group) 2301 { 2302 /* There is no group for this meta-data yet, so we create a new group */ 2303 bool age_restricted = meta.age_mask.bits != 0; 2304 const char *cipher; 2305 2306 group = GNUNET_new (struct GroupData); 2307 switch (meta.cipher) 2308 { 2309 case GNUNET_CRYPTO_BSA_RSA: 2310 cipher = age_restricted ? "RSA+age_restricted" : "RSA"; 2311 break; 2312 case GNUNET_CRYPTO_BSA_CS: 2313 cipher = age_restricted ? "CS+age_restricted" : "CS"; 2314 break; 2315 default: 2316 GNUNET_assert (false); 2317 } 2318 /* Create a new array for the denominations in this group */ 2319 group->list = json_array (); 2320 GNUNET_assert (NULL != group->list); 2321 group->json = GNUNET_JSON_PACK ( 2322 GNUNET_JSON_pack_string ("cipher", 2323 cipher), 2324 GNUNET_JSON_pack_array_steal ("denoms", 2325 group->list), 2326 TALER_JSON_PACK_DENOM_FEES ("fee", 2327 &meta.fees), 2328 TALER_JSON_pack_amount ("value", 2329 &meta.value)); 2330 GNUNET_assert (NULL != group->json); 2331 if (age_restricted) 2332 { 2333 GNUNET_assert ( 2334 0 == 2335 json_object_set_new (group->json, 2336 "age_mask", 2337 json_integer ( 2338 meta.age_mask.bits))); 2339 /* Remember that we have found at least _one_ age restricted denomination */ 2340 has_age_restricted_denomination = true; 2341 } 2342 group->group_off 2343 = json_array_size (grouped_denominations); 2344 GNUNET_assert (0 == 2345 json_array_append_new ( 2346 grouped_denominations, 2347 group->json)); 2348 GNUNET_assert ( 2349 GNUNET_OK == 2350 GNUNET_CONTAINER_multihashmap_put (denominations_by_group, 2351 &key, 2352 group, 2353 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); 2354 } 2355 2356 /* Now that we have found/created the right group, add the 2357 denomination to the list */ 2358 { 2359 struct GNUNET_JSON_PackSpec key_spec; 2360 bool private_key_lost; 2361 2362 private_key_lost 2363 = TEH_SECMOD_denom_priv_check_lost (&dk->h_denom_pub); 2364 switch (meta.cipher) 2365 { 2366 case GNUNET_CRYPTO_BSA_RSA: 2367 key_spec = 2368 GNUNET_JSON_pack_rsa_public_key ( 2369 "rsa_pub", 2370 dk->denom_pub.bsign_pub_key->details.rsa_public_key); 2371 break; 2372 case GNUNET_CRYPTO_BSA_CS: 2373 key_spec = 2374 GNUNET_JSON_pack_data_varsize ( 2375 "cs_pub", 2376 &dk->denom_pub.bsign_pub_key->details.cs_public_key, 2377 sizeof (dk->denom_pub.bsign_pub_key->details.cs_public_key)); 2378 break; 2379 default: 2380 GNUNET_assert (false); 2381 } 2382 2383 entry = GNUNET_JSON_PACK ( 2384 GNUNET_JSON_pack_data_auto ("master_sig", 2385 &dk->master_sig), 2386 GNUNET_JSON_pack_allow_null ( 2387 private_key_lost 2388 ? GNUNET_JSON_pack_bool ("lost", 2389 true) 2390 : GNUNET_JSON_pack_string ("dummy", 2391 NULL)), 2392 GNUNET_JSON_pack_timestamp ("stamp_start", 2393 dk->meta.start), 2394 GNUNET_JSON_pack_timestamp ("stamp_expire_withdraw", 2395 dk->meta.expire_withdraw), 2396 GNUNET_JSON_pack_timestamp ("stamp_expire_deposit", 2397 dk->meta.expire_deposit), 2398 GNUNET_JSON_pack_timestamp ("stamp_expire_legal", 2399 dk->meta.expire_legal), 2400 key_spec 2401 ); 2402 GNUNET_assert (NULL != entry); 2403 } 2404 2405 /* Build up the running hash of all master signatures of the 2406 denominations */ 2407 append_signature (&sig_ctx, 2408 group->group_off, 2409 (unsigned int) json_array_size (group->list), 2410 &dk->master_sig); 2411 /* Finally, add the denomination to the list of denominations in this 2412 group */ 2413 GNUNET_assert (json_is_array (group->list)); 2414 GNUNET_assert (0 == 2415 json_array_append_new (group->list, 2416 entry)); 2417 } 2418 } /* loop over heap ends */ 2419 2420 GNUNET_CONTAINER_multihashmap_iterate (denominations_by_group, 2421 &free_group, 2422 NULL); 2423 GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group); 2424 } 2425 GNUNET_CONTAINER_heap_destroy (heap); 2426 2427 if (! GNUNET_TIME_absolute_is_zero (last_cherry_pick_date.abs_time)) 2428 { 2429 struct GNUNET_HashCode hc; 2430 2431 compute_msig_hash (&sig_ctx, 2432 &hc); 2433 if (GNUNET_OK != 2434 create_krd (ksh, 2435 &hc, 2436 last_cherry_pick_date, 2437 sctx.signkeys, 2438 recoup, 2439 grouped_denominations)) 2440 { 2441 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2442 "Failed to generate key response data for %s\n", 2443 GNUNET_TIME_timestamp2s (last_cherry_pick_date)); 2444 goto CLEANUP; 2445 } 2446 ksh->management_only = false; 2447 2448 /* Sanity check: Make sure that age restriction is enabled IFF at least 2449 * one age restricted denomination exist */ 2450 if (! has_age_restricted_denomination && TEH_age_restriction_enabled) 2451 { 2452 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2453 "Age restriction is enabled, but NO denominations with age restriction found!\n"); 2454 goto CLEANUP; 2455 } 2456 else if (has_age_restricted_denomination && ! TEH_age_restriction_enabled) 2457 { 2458 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2459 "Age restriction is NOT enabled, but denominations with age restriction found!\n") 2460 ; 2461 goto CLEANUP; 2462 } 2463 } 2464 else 2465 { 2466 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2467 "No denomination keys available. Refusing to generate /keys response.\n"); 2468 } 2469 ret = GNUNET_OK; 2470 2471 CLEANUP: 2472 GNUNET_array_grow (sig_ctx.elements, 2473 sig_ctx.elements_size, 2474 0); 2475 json_decref (grouped_denominations); 2476 if (NULL != sctx.signkeys) 2477 json_decref (sctx.signkeys); 2478 json_decref (recoup); 2479 return ret; 2480 } 2481 2482 2483 /** 2484 * Called with information about global fees. 2485 * 2486 * @param ksh `struct TEH_KeyStateHandle *` we are building 2487 * @param fees the global fees we charge 2488 * @param purse_timeout when do purses time out 2489 * @param history_expiration how long are account histories preserved 2490 * @param purse_account_limit how many purses are free per account 2491 * @param start_date from when are these fees valid (start date) 2492 * @param end_date until when are these fees valid (end date, exclusive) 2493 * @param master_sig master key signature affirming that this is the correct 2494 * fee (of purpose #TALER_SIGNATURE_MASTER_GLOBAL_FEES) 2495 */ 2496 static void 2497 global_fee_info_cb ( 2498 struct TEH_KeyStateHandle *ksh, 2499 const struct TALER_GlobalFeeSet *fees, 2500 struct GNUNET_TIME_Relative purse_timeout, 2501 struct GNUNET_TIME_Relative history_expiration, 2502 uint32_t purse_account_limit, 2503 struct GNUNET_TIME_Timestamp start_date, 2504 struct GNUNET_TIME_Timestamp end_date, 2505 const struct TALER_MasterSignatureP *master_sig) 2506 { 2507 struct TEH_GlobalFee *gf; 2508 2509 if (GNUNET_OK != 2510 TALER_exchange_offline_global_fee_verify ( 2511 start_date, 2512 end_date, 2513 fees, 2514 purse_timeout, 2515 history_expiration, 2516 purse_account_limit, 2517 &TEH_master_public_key, 2518 master_sig)) 2519 { 2520 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2521 "Database has global fee with invalid signature. Skipping entry. Did the exchange offline public key change?\n"); 2522 return; 2523 } 2524 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2525 "Found global fees with %u purses\n", 2526 purse_account_limit); 2527 gf = GNUNET_new (struct TEH_GlobalFee); 2528 gf->start_date = start_date; 2529 gf->end_date = end_date; 2530 gf->fees = *fees; 2531 gf->purse_timeout = purse_timeout; 2532 gf->history_expiration = history_expiration; 2533 gf->purse_account_limit = purse_account_limit; 2534 gf->master_sig = *master_sig; 2535 GNUNET_CONTAINER_DLL_insert (ksh->gf_head, 2536 ksh->gf_tail, 2537 gf); 2538 GNUNET_assert ( 2539 0 == 2540 json_array_append_new ( 2541 ksh->global_fees, 2542 GNUNET_JSON_PACK ( 2543 GNUNET_JSON_pack_timestamp ("start_date", 2544 start_date), 2545 GNUNET_JSON_pack_timestamp ("end_date", 2546 end_date), 2547 TALER_JSON_PACK_GLOBAL_FEES (fees), 2548 GNUNET_JSON_pack_time_rel ("history_expiration", 2549 history_expiration), 2550 GNUNET_JSON_pack_time_rel ("purse_timeout", 2551 purse_timeout), 2552 GNUNET_JSON_pack_uint64 ("purse_account_limit", 2553 purse_account_limit), 2554 GNUNET_JSON_pack_data_auto ("master_sig", 2555 master_sig)))); 2556 } 2557 2558 2559 /** 2560 * Create a key state. 2561 * 2562 * @param management_only if we should NOT run 'finish_keys_response()' 2563 * because we only need the state for the /management/keys API 2564 * @return NULL on error (i.e. failed to access database) 2565 */ 2566 static struct TEH_KeyStateHandle * 2567 build_key_state (bool management_only) 2568 { 2569 struct TEH_KeyStateHandle *ksh; 2570 enum GNUNET_DB_QueryStatus qs; 2571 2572 ksh = GNUNET_new (struct TEH_KeyStateHandle); 2573 ksh->signature_expires = GNUNET_TIME_UNIT_FOREVER_TS; 2574 ksh->reload_time = GNUNET_TIME_timestamp_get (); 2575 /* We must use the key_generation from when we STARTED the process! */ 2576 ksh->key_generation = key_generation; 2577 ksh->denomserial_map = GNUNET_CONTAINER_multihashmap32_create (1024); 2578 ksh->denomkey_map = GNUNET_CONTAINER_multihashmap_create (1024, 2579 true); 2580 ksh->signkey_map = GNUNET_CONTAINER_multipeermap_create (32, 2581 false /* MUST be false! */ 2582 ); 2583 ksh->auditors = json_array (); 2584 GNUNET_assert (NULL != ksh->auditors); 2585 /* NOTE: fetches master-signed signkeys, but ALSO those that were revoked! */ 2586 GNUNET_break (GNUNET_OK == 2587 TALER_EXCHANGEDB_preflight (TEH_pg)); 2588 if (NULL != ksh->global_fees) 2589 json_decref (ksh->global_fees); 2590 ksh->global_fees = json_array (); 2591 qs = TALER_EXCHANGEDB_iterate_global_fees (TEH_pg, 2592 &global_fee_info_cb, 2593 ksh); 2594 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2595 "Loading global fees from DB: %d\n", 2596 qs); 2597 if (qs < 0) 2598 { 2599 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); 2600 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); 2601 destroy_key_state (ksh); 2602 return NULL; 2603 } 2604 qs = TALER_EXCHANGEDB_iterate_denominations (TEH_pg, 2605 &denomination_info_cb, 2606 ksh); 2607 if (qs < 0) 2608 { 2609 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); 2610 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); 2611 destroy_key_state (ksh); 2612 return NULL; 2613 } 2614 /* NOTE: ONLY fetches non-revoked AND master-signed signkeys! */ 2615 qs = TALER_EXCHANGEDB_iterate_active_signkeys (TEH_pg, 2616 &signkey_info_cb, 2617 ksh); 2618 if (qs < 0) 2619 { 2620 GNUNET_break (0); 2621 destroy_key_state (ksh); 2622 return NULL; 2623 } 2624 qs = TALER_EXCHANGEDB_iterate_auditor_denominations (TEH_pg, 2625 &auditor_denom_cb, 2626 ksh); 2627 if (qs < 0) 2628 { 2629 GNUNET_break (0); 2630 destroy_key_state (ksh); 2631 return NULL; 2632 } 2633 qs = TALER_EXCHANGEDB_iterate_active_auditors (TEH_pg, 2634 &auditor_info_cb, 2635 ksh); 2636 if (qs < 0) 2637 { 2638 GNUNET_break (0); 2639 destroy_key_state (ksh); 2640 return NULL; 2641 } 2642 2643 if (management_only) 2644 { 2645 ksh->management_only = true; 2646 return ksh; 2647 } 2648 2649 if (GNUNET_OK != 2650 finish_keys_response (ksh)) 2651 { 2652 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2653 "Could not finish /keys response (required data not configured yet)\n"); 2654 destroy_key_state (ksh); 2655 return NULL; 2656 } 2657 TEH_resume_keys_requests (false); 2658 return ksh; 2659 } 2660 2661 2662 void 2663 TEH_keys_update_states () 2664 { 2665 struct GNUNET_DB_EventHeaderP es = { 2666 .size = htons (sizeof (es)), 2667 .type = htons (TALER_DBEVENT_EXCHANGE_KEYS_UPDATED), 2668 }; 2669 2670 TALER_EXCHANGEDB_event_notify (TEH_pg, 2671 &es, 2672 NULL, 2673 0); 2674 key_generation++; 2675 TEH_resume_keys_requests (false); 2676 } 2677 2678 2679 static struct TEH_KeyStateHandle * 2680 keys_get_state (bool management_only) 2681 { 2682 struct TEH_KeyStateHandle *old_ksh; 2683 struct TEH_KeyStateHandle *ksh; 2684 2685 old_ksh = key_state; 2686 if (NULL == old_ksh) 2687 { 2688 ksh = build_key_state (management_only); 2689 if (NULL == ksh) 2690 return NULL; 2691 key_state = ksh; 2692 return ksh; 2693 } 2694 if ( (old_ksh->key_generation < key_generation) || 2695 (GNUNET_TIME_absolute_is_past (old_ksh->signature_expires.abs_time)) ) 2696 { 2697 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2698 "Rebuilding /keys, generation upgrade from %llu to %llu\n", 2699 (unsigned long long) old_ksh->key_generation, 2700 (unsigned long long) key_generation); 2701 ksh = build_key_state (management_only); 2702 key_state = ksh; 2703 destroy_key_state (old_ksh); 2704 return ksh; 2705 } 2706 TEH_SECMOD_sync_key_helpers (); 2707 return old_ksh; 2708 } 2709 2710 2711 struct TEH_KeyStateHandle * 2712 TEH_keys_get_state_for_management_only (void) 2713 { 2714 return keys_get_state (true); 2715 } 2716 2717 2718 struct TEH_KeyStateHandle * 2719 TEH_keys_get_state (void) 2720 { 2721 struct TEH_KeyStateHandle *ksh; 2722 2723 ksh = keys_get_state (false); 2724 if (NULL == ksh) 2725 return NULL; 2726 2727 if (ksh->management_only) 2728 { 2729 if (GNUNET_OK != 2730 finish_keys_response (ksh)) 2731 return NULL; 2732 } 2733 2734 return ksh; 2735 } 2736 2737 2738 const struct TEH_GlobalFee * 2739 TEH_keys_global_fee_by_time ( 2740 struct TEH_KeyStateHandle *ksh, 2741 struct GNUNET_TIME_Timestamp ts) 2742 { 2743 for (const struct TEH_GlobalFee *gf = ksh->gf_head; 2744 NULL != gf; 2745 gf = gf->next) 2746 { 2747 if (GNUNET_TIME_timestamp_cmp (ts, 2748 >=, 2749 gf->start_date) && 2750 GNUNET_TIME_timestamp_cmp (ts, 2751 <, 2752 gf->end_date)) 2753 return gf; 2754 } 2755 return NULL; 2756 } 2757 2758 2759 struct TEH_DenominationKey * 2760 TEH_keys_denomination_by_hash ( 2761 const struct TALER_DenominationHashP *h_denom_pub, 2762 struct MHD_Connection *conn, 2763 enum MHD_Result *mret) 2764 { 2765 struct TEH_KeyStateHandle *ksh; 2766 2767 ksh = TEH_keys_get_state (); 2768 if (NULL == ksh) 2769 { 2770 *mret = TALER_MHD_reply_with_error (conn, 2771 MHD_HTTP_SERVICE_UNAVAILABLE, 2772 TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, 2773 NULL); 2774 return NULL; 2775 } 2776 2777 return TEH_keys_denomination_by_hash_from_state (ksh, 2778 h_denom_pub, 2779 conn, 2780 mret); 2781 } 2782 2783 2784 struct TEH_DenominationKey * 2785 TEH_keys_denomination_by_hash_from_state ( 2786 const struct TEH_KeyStateHandle *ksh, 2787 const struct TALER_DenominationHashP *h_denom_pub, 2788 struct MHD_Connection *conn, 2789 enum MHD_Result *mret) 2790 { 2791 struct TEH_DenominationKey *dk; 2792 2793 dk = GNUNET_CONTAINER_multihashmap_get (ksh->denomkey_map, 2794 &h_denom_pub->hash); 2795 if (NULL == dk) 2796 { 2797 if (NULL == conn) 2798 return NULL; 2799 *mret = TEH_RESPONSE_reply_unknown_denom_pub_hash (conn, 2800 h_denom_pub); 2801 return NULL; 2802 } 2803 return dk; 2804 } 2805 2806 2807 struct TEH_DenominationKey * 2808 TEH_keys_denomination_by_serial_from_state ( 2809 const struct TEH_KeyStateHandle *ksh, 2810 uint64_t denom_serial) 2811 { 2812 struct TEH_DenominationKey *dk; 2813 uint32_t serial32 = (uint32_t) denom_serial; 2814 2815 GNUNET_assert (denom_serial == (uint64_t) serial32); 2816 dk = GNUNET_CONTAINER_multihashmap32_get (ksh->denomserial_map, 2817 serial32); 2818 return dk; 2819 } 2820 2821 2822 enum TALER_ErrorCode 2823 TEH_keys_exchange_sign_ ( 2824 const struct GNUNET_CRYPTO_SignaturePurpose *purpose, 2825 struct TALER_ExchangePublicKeyP *pub, 2826 struct TALER_ExchangeSignatureP *sig) 2827 { 2828 struct TEH_KeyStateHandle *ksh; 2829 2830 ksh = TEH_keys_get_state (); 2831 if (NULL == ksh) 2832 { 2833 /* This *can* happen if the exchange's crypto helper is not running 2834 or had some bad error. */ 2835 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2836 "Cannot sign request, no valid signing keys available.\n"); 2837 return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; 2838 } 2839 return TEH_keys_exchange_sign2_ (ksh, 2840 purpose, 2841 pub, 2842 sig); 2843 } 2844 2845 2846 enum TALER_ErrorCode 2847 TEH_keys_exchange_sign2_ ( 2848 void *cls, 2849 const struct GNUNET_CRYPTO_SignaturePurpose *purpose, 2850 struct TALER_ExchangePublicKeyP *pub, 2851 struct TALER_ExchangeSignatureP *sig) 2852 { 2853 struct TEH_KeyStateHandle *ksh = cls; 2854 enum TALER_ErrorCode ec; 2855 2856 TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_EDDSA]++; 2857 ec = TEH_SECMOD_exchange_sign (purpose, 2858 pub, 2859 sig); 2860 if (TALER_EC_NONE != ec) 2861 return ec; 2862 { 2863 /* Here we check here that 'pub' is set to an exchange public key that is 2864 actually signed by the master key! Otherwise, we happily continue to 2865 use key material even if the offline signatures have not been made 2866 yet! */ 2867 struct GNUNET_PeerIdentity pid; 2868 struct SigningKey *sk; 2869 2870 pid.public_key = pub->eddsa_pub; 2871 sk = GNUNET_CONTAINER_multipeermap_get (ksh->signkey_map, 2872 &pid); 2873 if (NULL == sk) 2874 { 2875 /* just to be safe, zero out the (valid) signature, as the key 2876 should not or no longer be used */ 2877 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2878 "Cannot sign, offline key signatures are missing!\n"); 2879 memset (sig, 2880 0, 2881 sizeof (*sig)); 2882 return TALER_EC_EXCHANGE_SIGNKEY_HELPER_OFFLINE_MISSING; 2883 } 2884 } 2885 return ec; 2886 } 2887 2888 2889 /** 2890 * Comparator used for a binary search by cherry_pick_date for @a key in the 2891 * `struct KeysResponseData` array. See libc's qsort() and bsearch() functions. 2892 * 2893 * @param key pointer to a `struct GNUNET_TIME_Timestamp` 2894 * @param value pointer to a `struct KeysResponseData` array entry 2895 * @return 0 if time matches, -1 if key is smaller, 1 if key is larger 2896 */ 2897 static int 2898 krd_search_comparator (const void *key, 2899 const void *value) 2900 { 2901 const struct GNUNET_TIME_Timestamp *kd = key; 2902 const struct KeysResponseData *krd = value; 2903 2904 if (GNUNET_TIME_timestamp_cmp (*kd, 2905 >, 2906 krd->cherry_pick_date)) 2907 return -1; 2908 if (GNUNET_TIME_timestamp_cmp (*kd, 2909 <, 2910 krd->cherry_pick_date)) 2911 return 1; 2912 return 0; 2913 } 2914 2915 2916 enum MHD_Result 2917 TEH_keys_get_handler (struct TEH_RequestContext *rc, 2918 const char *const args[]) 2919 { 2920 struct GNUNET_TIME_Timestamp last_issue_date; 2921 const char *etag; 2922 2923 etag = MHD_lookup_connection_value (rc->connection, 2924 MHD_HEADER_KIND, 2925 MHD_HTTP_HEADER_IF_NONE_MATCH); 2926 (void) args; 2927 { 2928 const char *have_cherrypick; 2929 2930 have_cherrypick = MHD_lookup_connection_value (rc->connection, 2931 MHD_GET_ARGUMENT_KIND, 2932 "last_issue_date"); 2933 if (NULL != have_cherrypick) 2934 { 2935 unsigned long long cherrypickn; 2936 2937 if (1 != 2938 sscanf (have_cherrypick, 2939 "%llu", 2940 &cherrypickn)) 2941 { 2942 GNUNET_break_op (0); 2943 return TALER_MHD_reply_with_error (rc->connection, 2944 MHD_HTTP_BAD_REQUEST, 2945 TALER_EC_GENERIC_PARAMETER_MALFORMED, 2946 have_cherrypick); 2947 } 2948 /* The following multiplication may overflow; but this should not really 2949 be a problem, as giving back 'older' data than what the client asks for 2950 (given that the client asks for data in the distant future) is not 2951 problematic */ 2952 last_issue_date = GNUNET_TIME_timestamp_from_s (cherrypickn); 2953 } 2954 else 2955 { 2956 last_issue_date = GNUNET_TIME_UNIT_ZERO_TS; 2957 } 2958 } 2959 2960 { 2961 struct TEH_KeyStateHandle *ksh; 2962 const struct KeysResponseData *krd; 2963 2964 ksh = TEH_keys_get_state (); 2965 if ( (NULL == ksh) || 2966 (0 == ksh->krd_array_length) ) 2967 { 2968 if ( ( (SKR_LIMIT <= skr_size) && 2969 (GNUNET_TIME_relative_cmp ( 2970 GNUNET_TIME_absolute_get_duration (rc->start_time), 2971 >, 2972 GNUNET_TIME_UNIT_SECONDS)) ) || 2973 TEH_suicide) 2974 { 2975 return TALER_MHD_reply_with_error ( 2976 rc->connection, 2977 MHD_HTTP_SERVICE_UNAVAILABLE, 2978 TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, 2979 TEH_suicide 2980 ? "server terminating" 2981 : "too many connections suspended waiting on /keys"); 2982 } 2983 return suspend_request (rc->connection); 2984 } 2985 krd = bsearch (&last_issue_date, 2986 ksh->krd_array, 2987 ksh->krd_array_length, 2988 sizeof (struct KeysResponseData), 2989 &krd_search_comparator); 2990 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2991 "Filtering /keys by cherry pick date %s found entry %u/%u\n", 2992 GNUNET_TIME_timestamp2s (last_issue_date), 2993 (unsigned int) (krd - ksh->krd_array), 2994 ksh->krd_array_length); 2995 if ( (NULL == krd) && 2996 (ksh->krd_array_length > 0) ) 2997 { 2998 if (! GNUNET_TIME_absolute_is_zero (last_issue_date.abs_time)) 2999 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3000 "Client provided invalid cherry picking timestamp %s, returning full response\n", 3001 GNUNET_TIME_timestamp2s (last_issue_date)); 3002 krd = &ksh->krd_array[ksh->krd_array_length - 1]; 3003 } 3004 if (NULL == krd) 3005 { 3006 /* Likely keys not ready *yet*. 3007 Wait until they are. */ 3008 return suspend_request (rc->connection); 3009 } 3010 if ( (NULL != etag) && 3011 (0 == strcmp (etag, 3012 krd->etag)) ) 3013 return TEH_RESPONSE_reply_not_modified (rc->connection, 3014 krd->etag, 3015 &setup_general_response_headers, 3016 ksh); 3017 3018 return MHD_queue_response ( 3019 rc->connection, 3020 MHD_HTTP_OK, 3021 (TALER_MHD_CT_DEFLATE == 3022 TALER_MHD_can_compress (rc->connection, 3023 TALER_MHD_CT_DEFLATE)) 3024 ? krd->response_compressed 3025 : krd->response_uncompressed); 3026 } 3027 } 3028 3029 3030 /** 3031 * Closure for #add_future_denomkey_cb and #add_future_signkey_cb. 3032 */ 3033 struct FutureBuilderContext 3034 { 3035 /** 3036 * Our key state. 3037 */ 3038 struct TEH_KeyStateHandle *ksh; 3039 3040 /** 3041 * Array of denomination keys. 3042 */ 3043 json_t *denoms; 3044 3045 /** 3046 * Array of signing keys. 3047 */ 3048 json_t *signkeys; 3049 3050 }; 3051 3052 3053 /** 3054 * Function called on all of our current and future denomination keys 3055 * known to the helper process. Filters out those that are current 3056 * and adds the remaining denomination keys (with their configuration 3057 * data) to the JSON array. 3058 * 3059 * @param cls the `struct FutureBuilderContext *` 3060 * @param h_denom_pub hash of the denomination public key 3061 * @param value a `struct HelperDenomination` 3062 * @return #GNUNET_OK (continue to iterate) 3063 */ 3064 static enum GNUNET_GenericReturnValue 3065 add_future_denomkey_cb (void *cls, 3066 const struct GNUNET_HashCode *h_denom_pub, 3067 void *value) 3068 { 3069 struct FutureBuilderContext *fbc = cls; 3070 struct HelperDenomination *hd = value; 3071 struct TEH_DenominationKey *dk; 3072 struct TALER_EXCHANGEDB_DenominationKeyMetaData meta = {0}; 3073 3074 dk = GNUNET_CONTAINER_multihashmap_get (fbc->ksh->denomkey_map, 3075 h_denom_pub); 3076 if (NULL != dk) 3077 return GNUNET_OK; /* skip: this key is already active! */ 3078 if (GNUNET_TIME_relative_is_zero (hd->validity_duration)) 3079 return GNUNET_OK; /* this key already expired! */ 3080 meta.start = hd->start_time; 3081 meta.expire_withdraw = GNUNET_TIME_absolute_to_timestamp ( 3082 GNUNET_TIME_absolute_add (meta.start.abs_time, 3083 hd->validity_duration)); 3084 if (GNUNET_OK != 3085 TEH_CONFIG_load_denom_data (hd->section_name, 3086 &meta)) 3087 { 3088 /* Woops, couldn't determine fee structure!? */ 3089 return GNUNET_OK; 3090 } 3091 GNUNET_assert ( 3092 0 == 3093 json_array_append_new ( 3094 fbc->denoms, 3095 GNUNET_JSON_PACK ( 3096 TALER_JSON_pack_amount ("value", 3097 &meta.value), 3098 GNUNET_JSON_pack_timestamp ("stamp_start", 3099 meta.start), 3100 GNUNET_JSON_pack_timestamp ("stamp_expire_withdraw", 3101 meta.expire_withdraw), 3102 GNUNET_JSON_pack_timestamp ("stamp_expire_deposit", 3103 meta.expire_deposit), 3104 GNUNET_JSON_pack_timestamp ("stamp_expire_legal", 3105 meta.expire_legal), 3106 TALER_JSON_pack_denom_pub ("denom_pub", 3107 &hd->denom_pub), 3108 TALER_JSON_PACK_DENOM_FEES ("fee", 3109 &meta.fees), 3110 GNUNET_JSON_pack_data_auto ("denom_secmod_sig", 3111 &hd->sm_sig), 3112 GNUNET_JSON_pack_string ("section_name", 3113 hd->section_name)))); 3114 return GNUNET_OK; 3115 } 3116 3117 3118 /** 3119 * Function called on all of our current and future exchange signing keys 3120 * known to the helper process. Filters out those that are current 3121 * and adds the remaining signing keys (with their configuration 3122 * data) to the JSON array. 3123 * 3124 * @param cls the `struct FutureBuilderContext *` 3125 * @param pid actually the exchange public key (type disguised) 3126 * @param value a `struct HelperDenomination` 3127 * @return #GNUNET_OK (continue to iterate) 3128 */ 3129 static enum GNUNET_GenericReturnValue 3130 add_future_signkey_cb (void *cls, 3131 const struct GNUNET_PeerIdentity *pid, 3132 void *value) 3133 { 3134 struct FutureBuilderContext *fbc = cls; 3135 struct HelperSignkey *hsk = value; 3136 struct SigningKey *sk; 3137 struct GNUNET_TIME_Timestamp stamp_expire; 3138 struct GNUNET_TIME_Timestamp legal_end; 3139 3140 sk = GNUNET_CONTAINER_multipeermap_get (fbc->ksh->signkey_map, 3141 pid); 3142 if (NULL != sk) 3143 return GNUNET_OK; /* skip: this key is already active */ 3144 if (GNUNET_TIME_relative_is_zero (hsk->validity_duration)) 3145 return GNUNET_OK; /* this key already expired! */ 3146 stamp_expire = GNUNET_TIME_absolute_to_timestamp ( 3147 GNUNET_TIME_absolute_add (hsk->start_time.abs_time, 3148 hsk->validity_duration)); 3149 legal_end = GNUNET_TIME_absolute_to_timestamp ( 3150 GNUNET_TIME_absolute_add (stamp_expire.abs_time, 3151 signkey_legal_duration)); 3152 GNUNET_assert (0 == 3153 json_array_append_new ( 3154 fbc->signkeys, 3155 GNUNET_JSON_PACK ( 3156 GNUNET_JSON_pack_data_auto ("key", 3157 &hsk->exchange_pub), 3158 GNUNET_JSON_pack_timestamp ("stamp_start", 3159 hsk->start_time), 3160 GNUNET_JSON_pack_timestamp ("stamp_expire", 3161 stamp_expire), 3162 GNUNET_JSON_pack_timestamp ("stamp_end", 3163 legal_end), 3164 GNUNET_JSON_pack_data_auto ("signkey_secmod_sig", 3165 &hsk->sm_sig)))); 3166 return GNUNET_OK; 3167 } 3168 3169 3170 enum MHD_Result 3171 TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh, 3172 struct MHD_Connection *connection) 3173 { 3174 struct TEH_KeyStateHandle *ksh; 3175 json_t *reply; 3176 3177 (void) rh; 3178 ksh = TEH_keys_get_state_for_management_only (); 3179 if (NULL == ksh) 3180 { 3181 return TALER_MHD_reply_with_error (connection, 3182 MHD_HTTP_SERVICE_UNAVAILABLE, 3183 TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, 3184 "no key state"); 3185 } 3186 TEH_SECMOD_sync_key_helpers (); 3187 if (NULL == ksh->management_keys_reply) 3188 { 3189 struct FutureBuilderContext fbc = { 3190 .ksh = ksh, 3191 }; 3192 3193 if (! TEH_SECMOD_have_denom_sm_pub ()) 3194 { 3195 /* Either IPC failed, or neither helper had any denominations configured. */ 3196 return TALER_MHD_reply_with_error (connection, 3197 MHD_HTTP_BAD_GATEWAY, 3198 TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE, 3199 NULL); 3200 } 3201 if (! TEH_SECMOD_have_esign_sm_pub ()) 3202 { 3203 return TALER_MHD_reply_with_error (connection, 3204 MHD_HTTP_BAD_GATEWAY, 3205 TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE, 3206 NULL); 3207 } 3208 fbc.denoms = json_array (); 3209 GNUNET_assert (NULL != fbc.denoms); 3210 fbc.signkeys = json_array (); 3211 GNUNET_assert (NULL != fbc.signkeys); 3212 TEH_SECMOD_iterate_denom_keys (&add_future_denomkey_cb, 3213 &fbc); 3214 TEH_SECMOD_iterate_esign_keys (&add_future_signkey_cb, 3215 &fbc); 3216 reply = GNUNET_JSON_PACK ( 3217 GNUNET_JSON_pack_array_steal ("future_denoms", 3218 fbc.denoms), 3219 GNUNET_JSON_pack_array_steal ("future_signkeys", 3220 fbc.signkeys), 3221 GNUNET_JSON_pack_data_auto ("master_pub", 3222 &TEH_master_public_key)); 3223 { 3224 json_t *sj = TEH_SECMOD_get_sm_pubs_as_json (); 3225 3226 json_object_update (reply, 3227 sj); 3228 json_decref (sj); 3229 } 3230 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3231 "Returning GET /management/keys response:\n"); 3232 if (NULL == reply) 3233 { 3234 GNUNET_break (0); 3235 return TALER_MHD_reply_with_error (connection, 3236 MHD_HTTP_INTERNAL_SERVER_ERROR, 3237 TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE, 3238 NULL); 3239 } 3240 GNUNET_assert (NULL == ksh->management_keys_reply); 3241 ksh->management_keys_reply = reply; 3242 } 3243 else 3244 { 3245 reply = ksh->management_keys_reply; 3246 } 3247 return TALER_MHD_reply_json (connection, 3248 reply, 3249 MHD_HTTP_OK); 3250 } 3251 3252 3253 /* end of taler-exchange-httpd_keys.c */