taler-exchange-offline.c (162130B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2020-2026 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 taler-exchange-offline.c 18 * @brief Support for operations involving the exchange's offline master key. 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include <gnunet/gnunet_json_lib.h> 23 #include <gnunet/gnunet_util_lib.h> 24 #include "taler/taler_json_lib.h" 25 26 struct AmlStaffRequest; 27 #define TALER_EXCHANGE_POST_MANAGEMENT_AML_OFFICERS_RESULT_CLOSURE \ 28 struct AmlStaffRequest 29 #include "taler/exchange/post-management-aml-officers.h" 30 31 struct DenomRevocationRequest; 32 #define TALER_EXCHANGE_POST_MANAGEMENT_DENOMINATIONS_REVOKE_RESULT_CLOSURE \ 33 struct DenomRevocationRequest 34 #include \ 35 "taler/exchange/post-management-denominations-H_DENOM_PUB-revoke.h" 36 37 struct SignkeyRevocationRequest; 38 #define TALER_EXCHANGE_POST_MANAGEMENT_SIGNKEYS_REVOKE_RESULT_CLOSURE \ 39 struct SignkeyRevocationRequest 40 #include \ 41 "taler/exchange/post-management-signkeys-EXCHANGE_PUB-revoke.h" 42 43 struct AuditorAddRequest; 44 #define TALER_EXCHANGE_POST_MANAGEMENT_AUDITORS_RESULT_CLOSURE \ 45 struct AuditorAddRequest 46 #include "taler/exchange/post-management-auditors.h" 47 48 struct AuditorDelRequest; 49 #define TALER_EXCHANGE_POST_MANAGEMENT_AUDITORS_DISABLE_RESULT_CLOSURE \ 50 struct AuditorDelRequest 51 #include "taler/exchange/post-management-auditors-AUDITOR_PUB-disable.h" 52 53 struct WireAddRequest; 54 #define TALER_EXCHANGE_POST_MANAGEMENT_WIRE_RESULT_CLOSURE \ 55 struct WireAddRequest 56 #include "taler/exchange/post-management-wire.h" 57 58 struct WireDelRequest; 59 #define TALER_EXCHANGE_POST_MANAGEMENT_WIRE_DISABLE_RESULT_CLOSURE \ 60 struct WireDelRequest 61 #include "taler/exchange/post-management-wire-disable.h" 62 63 struct WireFeeRequest; 64 #define TALER_EXCHANGE_POST_MANAGEMENT_WIRE_FEES_RESULT_CLOSURE \ 65 struct WireFeeRequest 66 #include "taler/exchange/post-management-wire-fee.h" 67 68 struct GlobalFeeRequest; 69 #define TALER_EXCHANGE_POST_MANAGEMENT_GLOBAL_FEES_RESULT_CLOSURE \ 70 struct GlobalFeeRequest 71 #include "taler/exchange/post-management-global-fees.h" 72 73 struct DrainProfitsRequest; 74 #define TALER_EXCHANGE_POST_MANAGEMENT_DRAIN_RESULT_CLOSURE \ 75 struct DrainProfitsRequest 76 #include "taler/exchange/post-management-drain.h" 77 78 struct UploadKeysRequest; 79 #define TALER_EXCHANGE_POST_MANAGEMENT_KEYS_RESULT_CLOSURE \ 80 struct UploadKeysRequest 81 #include "taler/exchange/post-management-keys.h" 82 83 struct PartnerAddRequest; 84 #define TALER_EXCHANGE_POST_MANAGEMENT_PARTNERS_RESULT_CLOSURE \ 85 struct PartnerAddRequest 86 #include "taler/exchange/post-management-partners.h" 87 88 #define TALER_EXCHANGE_GET_MANAGEMENT_KEYS_RESULT_CLOSURE \ 89 char *const 90 #include "taler/exchange/get-management-keys.h" 91 92 #include <microhttpd.h> 93 #include <regex.h> 94 95 96 /** 97 * Name of the input for the 'sign' and 'show' operation. 98 * The last component --by convention-- identifies the protocol version 99 * and should be incremented whenever the JSON format of the 'argument' changes. 100 */ 101 #define OP_INPUT_KEYS "exchange-input-keys-0" 102 103 /** 104 * Name of the operation to 'disable auditor' 105 * The last component --by convention-- identifies the protocol version 106 * and should be incremented whenever the JSON format of the 'argument' changes. 107 */ 108 #define OP_DISABLE_AUDITOR "exchange-disable-auditor-0" 109 110 /** 111 * Name of the operation to 'enable auditor' 112 * The last component --by convention-- identifies the protocol version 113 * and should be incremented whenever the JSON format of the 'argument' changes. 114 */ 115 #define OP_ENABLE_AUDITOR "exchange-enable-auditor-0" 116 117 /** 118 * Name of the operation to 'enable wire' 119 * The last component --by convention-- identifies the protocol version 120 * and should be incremented whenever the JSON format of the 'argument' changes. 121 */ 122 #define OP_ENABLE_WIRE "exchange-enable-wire-0" 123 124 /** 125 * Name of the operation to 'disable wire' 126 * The last component --by convention-- identifies the protocol version 127 * and should be incremented whenever the JSON format of the 'argument' changes. 128 */ 129 #define OP_DISABLE_WIRE "exchange-disable-wire-0" 130 131 /** 132 * Name of the operation to set a 'wire-fee' 133 * The last component --by convention-- identifies the protocol version 134 * and should be incremented whenever the JSON format of the 'argument' changes. 135 */ 136 #define OP_SET_WIRE_FEE "exchange-set-wire-fee-0" 137 138 /** 139 * Name of the operation to set a 'global-fee' 140 * The last component --by convention-- identifies the protocol version 141 * and should be incremented whenever the JSON format of the 'argument' changes. 142 */ 143 #define OP_SET_GLOBAL_FEE "exchange-set-global-fee-0" 144 145 /** 146 * Name of the operation to 'upload' key signatures 147 * The last component --by convention-- identifies the protocol version 148 * and should be incremented whenever the JSON format of the 'argument' changes. 149 */ 150 #define OP_UPLOAD_SIGS "exchange-upload-sigs-0" 151 152 /** 153 * Name of the operation to 'revoke-denomination' key 154 * The last component --by convention-- identifies the protocol version 155 * and should be incremented whenever the JSON format of the 'argument' changes. 156 */ 157 #define OP_REVOKE_DENOMINATION "exchange-revoke-denomination-0" 158 159 /** 160 * Name of the operation to 'revoke-signkey' 161 * The last component --by convention-- identifies the protocol version 162 * and should be incremented whenever the JSON format of the 'argument' changes. 163 */ 164 #define OP_REVOKE_SIGNKEY "exchange-revoke-signkey-0" 165 166 /** 167 * Show the offline signing key. 168 * The last component --by convention-- identifies the protocol version 169 * and should be incremented whenever the JSON format of the 'argument' changes. 170 */ 171 #define OP_SETUP "exchange-setup-0" 172 173 /** 174 * Generate message to drain profits. 175 */ 176 #define OP_DRAIN_PROFITS "exchange-drain-profits-0" 177 178 /** 179 * Setup AML staff. 180 */ 181 #define OP_UPDATE_AML_STAFF "exchange-add-aml-staff-0" 182 183 /** 184 * Setup partner exchange for wad transfers. 185 */ 186 #define OP_ADD_PARTNER "exchange-add-partner-0" 187 188 /** 189 * Our private key, initialized in #load_offline_key(). 190 */ 191 static struct TALER_MasterPrivateKeyP master_priv; 192 193 /** 194 * Our public key, initialized in #load_offline_key(). 195 */ 196 static struct TALER_MasterPublicKeyP master_pub; 197 198 /** 199 * Our context for making HTTP requests. 200 */ 201 static struct GNUNET_CURL_Context *ctx; 202 203 /** 204 * Reschedule context for #ctx. 205 */ 206 static struct GNUNET_CURL_RescheduleContext *rc; 207 208 /** 209 * Handle to the exchange's configuration 210 */ 211 static const struct GNUNET_CONFIGURATION_Handle *kcfg; 212 213 /** 214 * Return value from main(). 215 */ 216 static int global_ret; 217 218 /** 219 * Input to consume. 220 */ 221 static json_t *in; 222 223 /** 224 * Array of actions to perform. 225 */ 226 static json_t *out; 227 228 /** 229 * Currency we have configured. 230 */ 231 static char *currency; 232 233 /** 234 * URL of the exchange we are interacting with 235 * as per our configuration. 236 */ 237 static char *CFG_exchange_url; 238 239 /** 240 * A subcommand supported by this program. 241 */ 242 struct SubCommand 243 { 244 /** 245 * Name of the command. 246 */ 247 const char *name; 248 249 /** 250 * Help text for the command. 251 */ 252 const char *help; 253 254 /** 255 * Function implementing the command. 256 * 257 * @param args subsequent command line arguments (char **) 258 */ 259 void (*cb)(char *const *args); 260 }; 261 262 263 /** 264 * Data structure for denomination revocation requests. 265 */ 266 struct DenomRevocationRequest 267 { 268 269 /** 270 * Kept in a DLL. 271 */ 272 struct DenomRevocationRequest *next; 273 274 /** 275 * Kept in a DLL. 276 */ 277 struct DenomRevocationRequest *prev; 278 279 /** 280 * Operation handle. 281 */ 282 struct TALER_EXCHANGE_PostManagementDenominationsRevokeHandle *h; 283 284 /** 285 * Array index of the associated command. 286 */ 287 size_t idx; 288 }; 289 290 291 /** 292 * Data structure for signkey revocation requests. 293 */ 294 struct SignkeyRevocationRequest 295 { 296 297 /** 298 * Kept in a DLL. 299 */ 300 struct SignkeyRevocationRequest *next; 301 302 /** 303 * Kept in a DLL. 304 */ 305 struct SignkeyRevocationRequest *prev; 306 307 /** 308 * Operation handle. 309 */ 310 struct TALER_EXCHANGE_PostManagementSignkeysRevokeHandle *h; 311 312 /** 313 * Array index of the associated command. 314 */ 315 size_t idx; 316 }; 317 318 319 /** 320 * Data structure for auditor add requests. 321 */ 322 struct AuditorAddRequest 323 { 324 325 /** 326 * Kept in a DLL. 327 */ 328 struct AuditorAddRequest *next; 329 330 /** 331 * Kept in a DLL. 332 */ 333 struct AuditorAddRequest *prev; 334 335 /** 336 * Operation handle. 337 */ 338 struct TALER_EXCHANGE_PostManagementAuditorsHandle *h; 339 340 /** 341 * Array index of the associated command. 342 */ 343 size_t idx; 344 }; 345 346 347 /** 348 * Data structure for auditor del requests. 349 */ 350 struct AuditorDelRequest 351 { 352 353 /** 354 * Kept in a DLL. 355 */ 356 struct AuditorDelRequest *next; 357 358 /** 359 * Kept in a DLL. 360 */ 361 struct AuditorDelRequest *prev; 362 363 /** 364 * Operation handle. 365 */ 366 struct TALER_EXCHANGE_PostManagementAuditorsDisableHandle *h; 367 368 /** 369 * Array index of the associated command. 370 */ 371 size_t idx; 372 }; 373 374 375 /** 376 * Data structure for wire add requests. 377 */ 378 struct WireAddRequest 379 { 380 381 /** 382 * Kept in a DLL. 383 */ 384 struct WireAddRequest *next; 385 386 /** 387 * Kept in a DLL. 388 */ 389 struct WireAddRequest *prev; 390 391 /** 392 * Operation handle. 393 */ 394 struct TALER_EXCHANGE_PostManagementWireHandle *h; 395 396 /** 397 * Array index of the associated command. 398 */ 399 size_t idx; 400 }; 401 402 403 /** 404 * Data structure for wire del requests. 405 */ 406 struct WireDelRequest 407 { 408 409 /** 410 * Kept in a DLL. 411 */ 412 struct WireDelRequest *next; 413 414 /** 415 * Kept in a DLL. 416 */ 417 struct WireDelRequest *prev; 418 419 /** 420 * Operation handle. 421 */ 422 struct TALER_EXCHANGE_PostManagementWireDisableHandle *h; 423 424 /** 425 * Array index of the associated command. 426 */ 427 size_t idx; 428 }; 429 430 431 /** 432 * Data structure for announcing wire fees. 433 */ 434 struct WireFeeRequest 435 { 436 437 /** 438 * Kept in a DLL. 439 */ 440 struct WireFeeRequest *next; 441 442 /** 443 * Kept in a DLL. 444 */ 445 struct WireFeeRequest *prev; 446 447 /** 448 * Operation handle. 449 */ 450 struct TALER_EXCHANGE_PostManagementWireFeesHandle *h; 451 452 /** 453 * Array index of the associated command. 454 */ 455 size_t idx; 456 }; 457 458 459 /** 460 * Data structure for draining profits. 461 */ 462 struct DrainProfitsRequest 463 { 464 465 /** 466 * Kept in a DLL. 467 */ 468 struct DrainProfitsRequest *next; 469 470 /** 471 * Kept in a DLL. 472 */ 473 struct DrainProfitsRequest *prev; 474 475 /** 476 * Operation handle. 477 */ 478 struct TALER_EXCHANGE_PostManagementDrainHandle *h; 479 480 /** 481 * Array index of the associated command. 482 */ 483 size_t idx; 484 }; 485 486 487 /** 488 * Data structure for announcing global fees. 489 */ 490 struct GlobalFeeRequest 491 { 492 493 /** 494 * Kept in a DLL. 495 */ 496 struct GlobalFeeRequest *next; 497 498 /** 499 * Kept in a DLL. 500 */ 501 struct GlobalFeeRequest *prev; 502 503 /** 504 * Operation handle. 505 */ 506 struct TALER_EXCHANGE_PostManagementGlobalFeesHandle *h; 507 508 /** 509 * Array index of the associated command. 510 */ 511 size_t idx; 512 }; 513 514 515 /** 516 * Ongoing /keys request. 517 */ 518 struct UploadKeysRequest 519 { 520 /** 521 * Kept in a DLL. 522 */ 523 struct UploadKeysRequest *next; 524 525 /** 526 * Kept in a DLL. 527 */ 528 struct UploadKeysRequest *prev; 529 530 /** 531 * Operation handle. 532 */ 533 struct TALER_EXCHANGE_PostManagementKeysHandle *h; 534 535 /** 536 * Operation index. 537 */ 538 size_t idx; 539 }; 540 541 542 /** 543 * Data structure for AML staff requests. 544 */ 545 struct AmlStaffRequest 546 { 547 548 /** 549 * Kept in a DLL. 550 */ 551 struct AmlStaffRequest *next; 552 553 /** 554 * Kept in a DLL. 555 */ 556 struct AmlStaffRequest *prev; 557 558 /** 559 * Operation handle. 560 */ 561 struct TALER_EXCHANGE_PostManagementAmlOfficersHandle *h; 562 563 /** 564 * Array index of the associated command. 565 */ 566 size_t idx; 567 }; 568 569 570 /** 571 * Data structure for partner add requests. 572 */ 573 struct PartnerAddRequest 574 { 575 576 /** 577 * Kept in a DLL. 578 */ 579 struct PartnerAddRequest *next; 580 581 /** 582 * Kept in a DLL. 583 */ 584 struct PartnerAddRequest *prev; 585 586 /** 587 * Operation handle. 588 */ 589 struct TALER_EXCHANGE_PostManagementPartnersHandle *h; 590 591 /** 592 * Array index of the associated command. 593 */ 594 size_t idx; 595 }; 596 597 598 /** 599 * Next work item to perform. 600 */ 601 static struct GNUNET_SCHEDULER_Task *nxt; 602 603 /** 604 * Handle for #do_download. 605 */ 606 static struct TALER_EXCHANGE_GetManagementKeysHandle *mgkh; 607 608 /** 609 * Active AML staff change requests. 610 */ 611 static struct AmlStaffRequest *asr_head; 612 613 /** 614 * Active AML staff change requests. 615 */ 616 static struct AmlStaffRequest *asr_tail; 617 618 /** 619 * Active partner add requests. 620 */ 621 static struct PartnerAddRequest *par_head; 622 623 /** 624 * Active partner add requests. 625 */ 626 static struct PartnerAddRequest *par_tail; 627 628 /** 629 * Active denomiantion revocation requests. 630 */ 631 static struct DenomRevocationRequest *drr_head; 632 633 /** 634 * Active denomiantion revocation requests. 635 */ 636 static struct DenomRevocationRequest *drr_tail; 637 638 /** 639 * Active signkey revocation requests. 640 */ 641 static struct SignkeyRevocationRequest *srr_head; 642 643 /** 644 * Active signkey revocation requests. 645 */ 646 static struct SignkeyRevocationRequest *srr_tail; 647 648 /** 649 * Active auditor add requests. 650 */ 651 static struct AuditorAddRequest *aar_head; 652 653 /** 654 * Active auditor add requests. 655 */ 656 static struct AuditorAddRequest *aar_tail; 657 658 /** 659 * Active auditor del requests. 660 */ 661 static struct AuditorDelRequest *adr_head; 662 663 /** 664 * Active auditor del requests. 665 */ 666 static struct AuditorDelRequest *adr_tail; 667 668 /** 669 * Active wire add requests. 670 */ 671 static struct WireAddRequest *war_head; 672 673 /** 674 * Active wire add requests. 675 */ 676 static struct WireAddRequest *war_tail; 677 678 /** 679 * Active wire del requests. 680 */ 681 static struct WireDelRequest *wdr_head; 682 683 /** 684 * Active wire del requests. 685 */ 686 static struct WireDelRequest *wdr_tail; 687 688 /** 689 * Active wire fee requests. 690 */ 691 static struct WireFeeRequest *wfr_head; 692 693 /** 694 * Active wire fee requests. 695 */ 696 static struct WireFeeRequest *wfr_tail; 697 698 /** 699 * Active global fee requests. 700 */ 701 static struct GlobalFeeRequest *gfr_head; 702 703 /** 704 * Active global fee requests. 705 */ 706 static struct GlobalFeeRequest *gfr_tail; 707 708 /** 709 * Active keys upload requests. 710 */ 711 static struct UploadKeysRequest *ukr_head; 712 713 /** 714 * Active keys upload requests. 715 */ 716 static struct UploadKeysRequest *ukr_tail; 717 718 /** 719 * Active drain profits requests. 720 */ 721 struct DrainProfitsRequest *dpr_head; 722 723 /** 724 * Active drain profits requests. 725 */ 726 static struct DrainProfitsRequest *dpr_tail; 727 728 729 /** 730 * Shutdown task. Invoked when the application is being terminated. 731 * 732 * @param cls NULL 733 */ 734 static void 735 do_shutdown (void *cls) 736 { 737 (void) cls; 738 739 { 740 struct AmlStaffRequest *asr; 741 742 while (NULL != (asr = asr_head)) 743 { 744 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 745 "Aborting incomplete AML staff update #%u\n", 746 (unsigned int) asr->idx); 747 TALER_EXCHANGE_post_management_aml_officers_cancel (asr->h); 748 GNUNET_CONTAINER_DLL_remove (asr_head, 749 asr_tail, 750 asr); 751 GNUNET_free (asr); 752 } 753 } 754 { 755 struct PartnerAddRequest *par; 756 757 while (NULL != (par = par_head)) 758 { 759 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 760 "Aborting incomplete partner add request #%u\n", 761 (unsigned int) par->idx); 762 TALER_EXCHANGE_post_management_partners_cancel (par->h); 763 GNUNET_CONTAINER_DLL_remove (par_head, 764 par_tail, 765 par); 766 GNUNET_free (par); 767 } 768 } 769 { 770 struct DenomRevocationRequest *drr; 771 772 while (NULL != (drr = drr_head)) 773 { 774 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 775 "Aborting incomplete denomination revocation #%u\n", 776 (unsigned int) drr->idx); 777 TALER_EXCHANGE_post_management_denominations_revoke_cancel (drr->h); 778 GNUNET_CONTAINER_DLL_remove (drr_head, 779 drr_tail, 780 drr); 781 GNUNET_free (drr); 782 } 783 } 784 { 785 struct SignkeyRevocationRequest *srr; 786 787 while (NULL != (srr = srr_head)) 788 { 789 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 790 "Aborting incomplete signkey revocation #%u\n", 791 (unsigned int) srr->idx); 792 TALER_EXCHANGE_post_management_signkeys_revoke_cancel (srr->h); 793 GNUNET_CONTAINER_DLL_remove (srr_head, 794 srr_tail, 795 srr); 796 GNUNET_free (srr); 797 } 798 } 799 800 { 801 struct AuditorAddRequest *aar; 802 803 while (NULL != (aar = aar_head)) 804 { 805 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 806 "Aborting incomplete auditor add #%u\n", 807 (unsigned int) aar->idx); 808 TALER_EXCHANGE_post_management_auditors_cancel (aar->h); 809 GNUNET_CONTAINER_DLL_remove (aar_head, 810 aar_tail, 811 aar); 812 GNUNET_free (aar); 813 } 814 } 815 { 816 struct AuditorDelRequest *adr; 817 818 while (NULL != (adr = adr_head)) 819 { 820 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 821 "Aborting incomplete auditor del #%u\n", 822 (unsigned int) adr->idx); 823 TALER_EXCHANGE_post_management_auditors_disable_cancel (adr->h); 824 GNUNET_CONTAINER_DLL_remove (adr_head, 825 adr_tail, 826 adr); 827 GNUNET_free (adr); 828 } 829 } 830 { 831 struct WireAddRequest *war; 832 833 while (NULL != (war = war_head)) 834 { 835 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 836 "Aborting incomplete wire add #%u\n", 837 (unsigned int) war->idx); 838 TALER_EXCHANGE_post_management_wire_cancel (war->h); 839 GNUNET_CONTAINER_DLL_remove (war_head, 840 war_tail, 841 war); 842 GNUNET_free (war); 843 } 844 } 845 { 846 struct WireDelRequest *wdr; 847 848 while (NULL != (wdr = wdr_head)) 849 { 850 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 851 "Aborting incomplete wire del #%u\n", 852 (unsigned int) wdr->idx); 853 TALER_EXCHANGE_post_management_wire_disable_cancel (wdr->h); 854 GNUNET_CONTAINER_DLL_remove (wdr_head, 855 wdr_tail, 856 wdr); 857 GNUNET_free (wdr); 858 } 859 } 860 { 861 struct WireFeeRequest *wfr; 862 863 while (NULL != (wfr = wfr_head)) 864 { 865 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 866 "Aborting incomplete wire fee #%u\n", 867 (unsigned int) wfr->idx); 868 TALER_EXCHANGE_post_management_wire_fees_cancel (wfr->h); 869 GNUNET_CONTAINER_DLL_remove (wfr_head, 870 wfr_tail, 871 wfr); 872 GNUNET_free (wfr); 873 } 874 } 875 { 876 struct GlobalFeeRequest *gfr; 877 878 while (NULL != (gfr = gfr_head)) 879 { 880 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 881 "Aborting incomplete global fee #%u\n", 882 (unsigned int) gfr->idx); 883 TALER_EXCHANGE_post_management_global_fees_cancel (gfr->h); 884 GNUNET_CONTAINER_DLL_remove (gfr_head, 885 gfr_tail, 886 gfr); 887 GNUNET_free (gfr); 888 } 889 } 890 { 891 struct UploadKeysRequest *ukr; 892 893 while (NULL != (ukr = ukr_head)) 894 { 895 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 896 "Aborting incomplete key signature upload #%u\n", 897 (unsigned int) ukr->idx); 898 TALER_EXCHANGE_post_management_keys_cancel (ukr->h); 899 GNUNET_CONTAINER_DLL_remove (ukr_head, 900 ukr_tail, 901 ukr); 902 GNUNET_free (ukr); 903 } 904 } 905 906 { 907 struct DrainProfitsRequest *dpr; 908 909 while (NULL != (dpr = dpr_head)) 910 { 911 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 912 "Aborting incomplete drain profits request #%u\n", 913 (unsigned int) dpr->idx); 914 TALER_EXCHANGE_post_management_drain_cancel (dpr->h); 915 GNUNET_CONTAINER_DLL_remove (dpr_head, 916 dpr_tail, 917 dpr); 918 GNUNET_free (dpr); 919 } 920 } 921 922 if (NULL != out) 923 { 924 if (EXIT_SUCCESS == global_ret) 925 json_dumpf (out, 926 stdout, 927 JSON_INDENT (2)); 928 json_decref (out); 929 out = NULL; 930 } 931 if (NULL != in) 932 { 933 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 934 "Input not consumed!\n"); 935 json_decref (in); 936 in = NULL; 937 } 938 if (NULL != nxt) 939 { 940 GNUNET_SCHEDULER_cancel (nxt); 941 nxt = NULL; 942 } 943 if (NULL != mgkh) 944 { 945 TALER_EXCHANGE_get_management_keys_cancel (mgkh); 946 mgkh = NULL; 947 } 948 if (NULL != ctx) 949 { 950 GNUNET_CURL_fini (ctx); 951 ctx = NULL; 952 } 953 if (NULL != rc) 954 { 955 GNUNET_CURL_gnunet_rc_destroy (rc); 956 rc = NULL; 957 } 958 } 959 960 961 /** 962 * Test if we should shut down because all tasks are done. 963 */ 964 static void 965 test_shutdown (void) 966 { 967 if ( (NULL == drr_head) && 968 (NULL == par_head) && 969 (NULL == asr_head) && 970 (NULL == srr_head) && 971 (NULL == aar_head) && 972 (NULL == adr_head) && 973 (NULL == war_head) && 974 (NULL == wdr_head) && 975 (NULL == wfr_head) && 976 (NULL == gfr_head) && 977 (NULL == ukr_head) && 978 (NULL == dpr_head) && 979 (NULL == mgkh) && 980 (NULL == nxt) ) 981 GNUNET_SCHEDULER_shutdown (); 982 } 983 984 985 /** 986 * Function to continue processing the next command. 987 * 988 * @param cls must be a `char *const*` with the array of 989 * command-line arguments to process next 990 */ 991 static void 992 work (void *cls); 993 994 995 /** 996 * Function to schedule job to process the next command. 997 * 998 * @param args the array of command-line arguments to process next 999 */ 1000 static void 1001 next (char *const *args) 1002 { 1003 GNUNET_assert (NULL == nxt); 1004 if (NULL == args[0]) 1005 { 1006 test_shutdown (); 1007 return; 1008 } 1009 nxt = GNUNET_SCHEDULER_add_now (&work, 1010 (void *) args); 1011 } 1012 1013 1014 /** 1015 * Add an operation to the #out JSON array for processing later. 1016 * 1017 * @param op_name name of the operation 1018 * @param op_value values for the operation (consumed) 1019 */ 1020 static void 1021 output_operation (const char *op_name, 1022 json_t *op_value) 1023 { 1024 json_t *action; 1025 1026 GNUNET_break (NULL != op_value); 1027 if (NULL == out) 1028 { 1029 out = json_array (); 1030 GNUNET_assert (NULL != out); 1031 } 1032 action = GNUNET_JSON_PACK ( 1033 GNUNET_JSON_pack_string ("operation", 1034 op_name), 1035 GNUNET_JSON_pack_object_steal ("arguments", 1036 op_value)); 1037 GNUNET_assert (0 == 1038 json_array_append_new (out, 1039 action)); 1040 } 1041 1042 1043 /** 1044 * Information about a subroutine for an upload. 1045 */ 1046 struct UploadHandler 1047 { 1048 /** 1049 * Key to trigger this subroutine. 1050 */ 1051 const char *key; 1052 1053 /** 1054 * Function implementing an upload. 1055 * 1056 * @param exchange_url URL of the exchange 1057 * @param idx index of the operation we are performing 1058 * @param value arguments to drive the upload. 1059 */ 1060 void (*cb)(const char *exchange_url, 1061 size_t idx, 1062 const json_t *value); 1063 1064 }; 1065 1066 1067 /** 1068 * Load the offline key (if not yet done). Triggers shutdown on failure. 1069 * 1070 * @param do_create #GNUNET_YES if the key may be created 1071 * @return #GNUNET_OK on success 1072 */ 1073 static enum GNUNET_GenericReturnValue 1074 load_offline_key (int do_create) 1075 { 1076 static bool done; 1077 int ret; 1078 char *fn; 1079 1080 if (done) 1081 return GNUNET_OK; 1082 if (GNUNET_OK != 1083 GNUNET_CONFIGURATION_get_value_filename (kcfg, 1084 "exchange-offline", 1085 "MASTER_PRIV_FILE", 1086 &fn)) 1087 { 1088 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1089 "exchange-offline", 1090 "MASTER_PRIV_FILE"); 1091 global_ret = EXIT_NOTCONFIGURED; 1092 test_shutdown (); 1093 return GNUNET_SYSERR; 1094 } 1095 if (GNUNET_YES != 1096 GNUNET_DISK_file_test (fn)) 1097 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1098 "Exchange master private key `%s' does not exist yet, creating it!\n", 1099 fn); 1100 ret = GNUNET_CRYPTO_eddsa_key_from_file (fn, 1101 do_create, 1102 &master_priv.eddsa_priv); 1103 if (GNUNET_SYSERR == ret) 1104 { 1105 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1106 "Failed to initialize master key from file `%s': %s\n", 1107 fn, 1108 "could not create file"); 1109 GNUNET_free (fn); 1110 global_ret = EXIT_FAILURE; 1111 test_shutdown (); 1112 return GNUNET_SYSERR; 1113 } 1114 GNUNET_free (fn); 1115 GNUNET_CRYPTO_eddsa_key_get_public (&master_priv.eddsa_priv, 1116 &master_pub.eddsa_pub); 1117 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1118 "Using master public key %s\n", 1119 TALER_B2S (&master_pub)); 1120 done = true; 1121 return GNUNET_OK; 1122 } 1123 1124 1125 /** 1126 * Function called with information about the post revocation operation result. 1127 * 1128 * @param drr the revocation request 1129 * @param dr response data 1130 */ 1131 static void 1132 denom_revocation_cb ( 1133 struct DenomRevocationRequest *drr, 1134 const struct TALER_EXCHANGE_PostManagementDenominationsRevokeResponse *dr) 1135 { 1136 const struct TALER_EXCHANGE_HttpResponse *hr = &dr->hr; 1137 1138 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1139 { 1140 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1141 "Upload failed for command %u with status %u: %s (%s)\n", 1142 (unsigned int) drr->idx, 1143 hr->http_status, 1144 hr->hint, 1145 TALER_JSON_get_error_hint (hr->reply)); 1146 global_ret = EXIT_FAILURE; 1147 } 1148 GNUNET_CONTAINER_DLL_remove (drr_head, 1149 drr_tail, 1150 drr); 1151 GNUNET_free (drr); 1152 test_shutdown (); 1153 } 1154 1155 1156 /** 1157 * Upload denomination revocation request data. 1158 * 1159 * @param exchange_url base URL of the exchange 1160 * @param idx index of the operation we are performing (for logging) 1161 * @param value arguments for denomination revocation 1162 */ 1163 static void 1164 upload_denom_revocation (const char *exchange_url, 1165 size_t idx, 1166 const json_t *value) 1167 { 1168 struct TALER_MasterSignatureP master_sig; 1169 struct TALER_DenominationHashP h_denom_pub; 1170 struct DenomRevocationRequest *drr; 1171 const char *err_name; 1172 unsigned int err_line; 1173 struct GNUNET_JSON_Specification spec[] = { 1174 GNUNET_JSON_spec_fixed_auto ("h_denom_pub", 1175 &h_denom_pub), 1176 GNUNET_JSON_spec_fixed_auto ("master_sig", 1177 &master_sig), 1178 GNUNET_JSON_spec_end () 1179 }; 1180 1181 if (GNUNET_OK != 1182 GNUNET_JSON_parse (value, 1183 spec, 1184 &err_name, 1185 &err_line)) 1186 { 1187 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1188 "Invalid input for denomination revocation: %s#%u at %u (skipping)\n", 1189 err_name, 1190 err_line, 1191 (unsigned int) idx); 1192 json_dumpf (value, 1193 stderr, 1194 JSON_INDENT (2)); 1195 global_ret = EXIT_FAILURE; 1196 GNUNET_SCHEDULER_shutdown (); 1197 return; 1198 } 1199 drr = GNUNET_new (struct DenomRevocationRequest); 1200 drr->idx = idx; 1201 drr->h = 1202 TALER_EXCHANGE_post_management_denominations_revoke_create (ctx, 1203 exchange_url, 1204 &h_denom_pub, 1205 &master_sig); 1206 TALER_EXCHANGE_post_management_denominations_revoke_start (drr->h, 1207 & 1208 denom_revocation_cb, 1209 drr); 1210 GNUNET_CONTAINER_DLL_insert (drr_head, 1211 drr_tail, 1212 drr); 1213 } 1214 1215 1216 /** 1217 * Function called with information about the post revocation operation result. 1218 * 1219 * @param srr the revocation request 1220 * @param sr response data 1221 */ 1222 static void 1223 signkey_revocation_cb ( 1224 struct SignkeyRevocationRequest *srr, 1225 const struct TALER_EXCHANGE_PostManagementSignkeysRevokeResponse *sr) 1226 { 1227 const struct TALER_EXCHANGE_HttpResponse *hr = &sr->hr; 1228 1229 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1230 { 1231 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1232 "Upload failed for command %u with status %u: %s (%s)\n", 1233 (unsigned int) srr->idx, 1234 hr->http_status, 1235 hr->hint, 1236 TALER_JSON_get_error_hint (hr->reply)); 1237 global_ret = EXIT_FAILURE; 1238 } 1239 GNUNET_CONTAINER_DLL_remove (srr_head, 1240 srr_tail, 1241 srr); 1242 GNUNET_free (srr); 1243 test_shutdown (); 1244 } 1245 1246 1247 /** 1248 * Upload signkey revocation request data. 1249 * 1250 * @param exchange_url base URL of the exchange 1251 * @param idx index of the operation we are performing (for logging) 1252 * @param value arguments for denomination revocation 1253 */ 1254 static void 1255 upload_signkey_revocation (const char *exchange_url, 1256 size_t idx, 1257 const json_t *value) 1258 { 1259 struct TALER_MasterSignatureP master_sig; 1260 struct TALER_ExchangePublicKeyP exchange_pub; 1261 struct SignkeyRevocationRequest *srr; 1262 const char *err_name; 1263 unsigned int err_line; 1264 struct GNUNET_JSON_Specification spec[] = { 1265 GNUNET_JSON_spec_fixed_auto ("exchange_pub", 1266 &exchange_pub), 1267 GNUNET_JSON_spec_fixed_auto ("master_sig", 1268 &master_sig), 1269 GNUNET_JSON_spec_end () 1270 }; 1271 1272 if (GNUNET_OK != 1273 GNUNET_JSON_parse (value, 1274 spec, 1275 &err_name, 1276 &err_line)) 1277 { 1278 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1279 "Invalid input for signkey revocation: %s#%u at %u (skipping)\n", 1280 err_name, 1281 err_line, 1282 (unsigned int) idx); 1283 json_dumpf (value, 1284 stderr, 1285 JSON_INDENT (2)); 1286 global_ret = EXIT_FAILURE; 1287 GNUNET_SCHEDULER_shutdown (); 1288 return; 1289 } 1290 srr = GNUNET_new (struct SignkeyRevocationRequest); 1291 srr->idx = idx; 1292 srr->h = 1293 TALER_EXCHANGE_post_management_signkeys_revoke_create (ctx, 1294 exchange_url, 1295 &exchange_pub, 1296 &master_sig); 1297 TALER_EXCHANGE_post_management_signkeys_revoke_start (srr->h, 1298 &signkey_revocation_cb, 1299 srr); 1300 GNUNET_CONTAINER_DLL_insert (srr_head, 1301 srr_tail, 1302 srr); 1303 } 1304 1305 1306 /** 1307 * Function called with information about the post auditor add operation result. 1308 * 1309 * @param aar the auditor add request 1310 * @param mer response data 1311 */ 1312 static void 1313 auditor_add_cb ( 1314 struct AuditorAddRequest *aar, 1315 const struct TALER_EXCHANGE_PostManagementAuditorsResponse *mer) 1316 { 1317 const struct TALER_EXCHANGE_HttpResponse *hr = &mer->hr; 1318 1319 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1320 { 1321 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1322 "Upload failed for command %u with status %u: %s (%s)\n", 1323 (unsigned int) aar->idx, 1324 hr->http_status, 1325 TALER_ErrorCode_get_hint (hr->ec), 1326 hr->hint); 1327 global_ret = EXIT_FAILURE; 1328 } 1329 GNUNET_CONTAINER_DLL_remove (aar_head, 1330 aar_tail, 1331 aar); 1332 GNUNET_free (aar); 1333 test_shutdown (); 1334 } 1335 1336 1337 /** 1338 * Upload auditor add data. 1339 * 1340 * @param exchange_url base URL of the exchange 1341 * @param idx index of the operation we are performing (for logging) 1342 * @param value arguments for denomination revocation 1343 */ 1344 static void 1345 upload_auditor_add (const char *exchange_url, 1346 size_t idx, 1347 const json_t *value) 1348 { 1349 struct TALER_MasterSignatureP master_sig; 1350 const char *auditor_url; 1351 const char *auditor_name; 1352 struct GNUNET_TIME_Timestamp start_time; 1353 struct TALER_AuditorPublicKeyP auditor_pub; 1354 struct AuditorAddRequest *aar; 1355 const char *err_name; 1356 unsigned int err_line; 1357 struct GNUNET_JSON_Specification spec[] = { 1358 TALER_JSON_spec_web_url ("auditor_url", 1359 &auditor_url), 1360 GNUNET_JSON_spec_string ("auditor_name", 1361 &auditor_name), 1362 GNUNET_JSON_spec_timestamp ("validity_start", 1363 &start_time), 1364 GNUNET_JSON_spec_fixed_auto ("auditor_pub", 1365 &auditor_pub), 1366 GNUNET_JSON_spec_fixed_auto ("master_sig", 1367 &master_sig), 1368 GNUNET_JSON_spec_end () 1369 }; 1370 1371 if (GNUNET_OK != 1372 GNUNET_JSON_parse (value, 1373 spec, 1374 &err_name, 1375 &err_line)) 1376 { 1377 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1378 "Invalid input for adding auditor: %s#%u at %u (skipping)\n", 1379 err_name, 1380 err_line, 1381 (unsigned int) idx); 1382 json_dumpf (value, 1383 stderr, 1384 JSON_INDENT (2)); 1385 global_ret = EXIT_FAILURE; 1386 GNUNET_SCHEDULER_shutdown (); 1387 return; 1388 } 1389 aar = GNUNET_new (struct AuditorAddRequest); 1390 aar->idx = idx; 1391 aar->h = 1392 TALER_EXCHANGE_post_management_auditors_create (ctx, 1393 exchange_url, 1394 &auditor_pub, 1395 auditor_url, 1396 auditor_name, 1397 start_time, 1398 &master_sig); 1399 TALER_EXCHANGE_post_management_auditors_start (aar->h, 1400 &auditor_add_cb, 1401 aar); 1402 GNUNET_CONTAINER_DLL_insert (aar_head, 1403 aar_tail, 1404 aar); 1405 } 1406 1407 1408 /** 1409 * Function called with information about the post auditor del operation result. 1410 * 1411 * @param adr auditor delete request 1412 * @param mdr response data 1413 */ 1414 static void 1415 auditor_del_cb ( 1416 struct AuditorDelRequest *adr, 1417 const struct TALER_EXCHANGE_PostManagementAuditorsDisableResponse *mdr) 1418 { 1419 const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr; 1420 1421 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1422 { 1423 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1424 "Upload failed for command %u with status %u: %s (%s)\n", 1425 (unsigned int) adr->idx, 1426 hr->http_status, 1427 TALER_ErrorCode_get_hint (hr->ec), 1428 hr->hint); 1429 global_ret = EXIT_FAILURE; 1430 } 1431 GNUNET_CONTAINER_DLL_remove (adr_head, 1432 adr_tail, 1433 adr); 1434 GNUNET_free (adr); 1435 test_shutdown (); 1436 } 1437 1438 1439 /** 1440 * Upload auditor del data. 1441 * 1442 * @param exchange_url base URL of the exchange 1443 * @param idx index of the operation we are performing (for logging) 1444 * @param value arguments for denomination revocation 1445 */ 1446 static void 1447 upload_auditor_del (const char *exchange_url, 1448 size_t idx, 1449 const json_t *value) 1450 { 1451 struct TALER_AuditorPublicKeyP auditor_pub; 1452 struct TALER_MasterSignatureP master_sig; 1453 struct GNUNET_TIME_Timestamp end_time; 1454 struct AuditorDelRequest *adr; 1455 const char *err_name; 1456 unsigned int err_line; 1457 struct GNUNET_JSON_Specification spec[] = { 1458 GNUNET_JSON_spec_fixed_auto ("auditor_pub", 1459 &auditor_pub), 1460 GNUNET_JSON_spec_timestamp ("validity_end", 1461 &end_time), 1462 GNUNET_JSON_spec_fixed_auto ("master_sig", 1463 &master_sig), 1464 GNUNET_JSON_spec_end () 1465 }; 1466 1467 if (GNUNET_OK != 1468 GNUNET_JSON_parse (value, 1469 spec, 1470 &err_name, 1471 &err_line)) 1472 { 1473 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1474 "Invalid input to disable auditor: %s#%u at %u (skipping)\n", 1475 err_name, 1476 err_line, 1477 (unsigned int) idx); 1478 json_dumpf (value, 1479 stderr, 1480 JSON_INDENT (2)); 1481 global_ret = EXIT_FAILURE; 1482 GNUNET_SCHEDULER_shutdown (); 1483 return; 1484 } 1485 adr = GNUNET_new (struct AuditorDelRequest); 1486 adr->idx = idx; 1487 adr->h = 1488 TALER_EXCHANGE_post_management_auditors_disable_create (ctx, 1489 exchange_url, 1490 &auditor_pub, 1491 end_time, 1492 &master_sig); 1493 TALER_EXCHANGE_post_management_auditors_disable_start (adr->h, 1494 &auditor_del_cb, 1495 adr); 1496 GNUNET_CONTAINER_DLL_insert (adr_head, 1497 adr_tail, 1498 adr); 1499 } 1500 1501 1502 /** 1503 * Function called with information about the post wire add operation result. 1504 * 1505 * @param war the wire add request 1506 * @param wer response data 1507 */ 1508 static void 1509 wire_add_cb ( 1510 struct WireAddRequest *war, 1511 const struct TALER_EXCHANGE_PostManagementWireResponse *wer) 1512 { 1513 const struct TALER_EXCHANGE_HttpResponse *hr = &wer->hr; 1514 1515 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1516 { 1517 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1518 "Upload failed for command %u with status %u: %s (%s)\n", 1519 (unsigned int) war->idx, 1520 hr->http_status, 1521 TALER_ErrorCode_get_hint (hr->ec), 1522 hr->hint); 1523 global_ret = EXIT_FAILURE; 1524 } 1525 GNUNET_CONTAINER_DLL_remove (war_head, 1526 war_tail, 1527 war); 1528 GNUNET_free (war); 1529 test_shutdown (); 1530 } 1531 1532 1533 /** 1534 * Upload wire add data. 1535 * 1536 * @param exchange_url base URL of the exchange 1537 * @param idx index of the operation we are performing (for logging) 1538 * @param value arguments for denomination revocation 1539 */ 1540 static void 1541 upload_wire_add (const char *exchange_url, 1542 size_t idx, 1543 const json_t *value) 1544 { 1545 struct TALER_MasterSignatureP master_sig_add; 1546 struct TALER_MasterSignatureP master_sig_wire; 1547 struct TALER_FullPayto payto_uri; 1548 struct GNUNET_TIME_Timestamp start_time; 1549 struct WireAddRequest *war; 1550 const char *err_name; 1551 const char *conversion_url = NULL; 1552 const char *open_banking_gateway = NULL; 1553 const char *prepared_transfer_url = NULL; 1554 const char *bank_label = NULL; 1555 int64_t priority = 0; 1556 const json_t *debit_restrictions; 1557 const json_t *credit_restrictions; 1558 unsigned int err_line; 1559 struct GNUNET_JSON_Specification spec[] = { 1560 TALER_JSON_spec_full_payto_uri ("payto_uri", 1561 &payto_uri), 1562 GNUNET_JSON_spec_mark_optional ( 1563 TALER_JSON_spec_web_url ("conversion_url", 1564 &conversion_url), 1565 NULL), 1566 GNUNET_JSON_spec_mark_optional ( 1567 TALER_JSON_spec_web_url ("open_banking_gateway", 1568 &open_banking_gateway), 1569 NULL), 1570 GNUNET_JSON_spec_mark_optional ( 1571 TALER_JSON_spec_web_url ("prepared_transfer_url", 1572 &prepared_transfer_url), 1573 NULL), 1574 GNUNET_JSON_spec_mark_optional ( 1575 GNUNET_JSON_spec_string ("bank_label", 1576 &bank_label), 1577 NULL), 1578 GNUNET_JSON_spec_int64 ("priority", 1579 &priority), 1580 GNUNET_JSON_spec_array_const ("debit_restrictions", 1581 &debit_restrictions), 1582 GNUNET_JSON_spec_array_const ("credit_restrictions", 1583 &credit_restrictions), 1584 GNUNET_JSON_spec_timestamp ("validity_start", 1585 &start_time), 1586 GNUNET_JSON_spec_fixed_auto ("master_sig_add", 1587 &master_sig_add), 1588 GNUNET_JSON_spec_fixed_auto ("master_sig_wire", 1589 &master_sig_wire), 1590 GNUNET_JSON_spec_end () 1591 }; 1592 1593 if (GNUNET_OK != 1594 GNUNET_JSON_parse (value, 1595 spec, 1596 &err_name, 1597 &err_line)) 1598 { 1599 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1600 "Invalid input for adding wire account: %s#%u at %u (skipping)\n", 1601 err_name, 1602 err_line, 1603 (unsigned int) idx); 1604 json_dumpf (value, 1605 stderr, 1606 JSON_INDENT (2)); 1607 global_ret = EXIT_FAILURE; 1608 GNUNET_SCHEDULER_shutdown (); 1609 return; 1610 } 1611 { 1612 char *wire_method; 1613 1614 wire_method = TALER_payto_get_method (payto_uri.full_payto); 1615 if (NULL == wire_method) 1616 { 1617 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1618 "payto:// URI `%s' is malformed\n", 1619 payto_uri.full_payto); 1620 global_ret = EXIT_FAILURE; 1621 GNUNET_SCHEDULER_shutdown (); 1622 return; 1623 } 1624 GNUNET_free (wire_method); 1625 } 1626 { 1627 char *msg = TALER_payto_validate (payto_uri); 1628 1629 if (NULL != msg) 1630 { 1631 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1632 "payto URI is malformed: %s\n", 1633 msg); 1634 GNUNET_free (msg); 1635 GNUNET_SCHEDULER_shutdown (); 1636 global_ret = EXIT_INVALIDARGUMENT; 1637 return; 1638 } 1639 } 1640 war = GNUNET_new (struct WireAddRequest); 1641 war->idx = idx; 1642 war->h = 1643 TALER_EXCHANGE_post_management_wire_create (ctx, 1644 exchange_url, 1645 payto_uri, 1646 start_time, 1647 &master_sig_add, 1648 &master_sig_wire); 1649 TALER_EXCHANGE_post_management_wire_set_options ( 1650 war->h, 1651 TALER_EXCHANGE_post_management_wire_option_bank_label (bank_label), 1652 TALER_EXCHANGE_post_management_wire_option_conversion_url (conversion_url), 1653 TALER_EXCHANGE_post_management_wire_option_open_banking_gateway ( 1654 open_banking_gateway), 1655 TALER_EXCHANGE_post_management_wire_option_prepared_transfer_url ( 1656 prepared_transfer_url), 1657 TALER_EXCHANGE_post_management_wire_option_debit_restrictions ( 1658 debit_restrictions), 1659 TALER_EXCHANGE_post_management_wire_option_credit_restrictions ( 1660 credit_restrictions), 1661 TALER_EXCHANGE_post_management_wire_option_priority (priority)); 1662 TALER_EXCHANGE_post_management_wire_start (war->h, 1663 &wire_add_cb, 1664 war); 1665 GNUNET_CONTAINER_DLL_insert (war_head, 1666 war_tail, 1667 war); 1668 } 1669 1670 1671 /** 1672 * Function called with information about the post wire del operation result. 1673 * 1674 * @param wdr request to delete wire account 1675 * @param wdres response data 1676 */ 1677 static void 1678 wire_del_cb ( 1679 struct WireDelRequest *wdr, 1680 const struct TALER_EXCHANGE_PostManagementWireDisableResponse *wdres) 1681 { 1682 const struct TALER_EXCHANGE_HttpResponse *hr = &wdres->hr; 1683 1684 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1685 { 1686 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1687 "Upload failed for command %u with status %u: %s (%s)\n", 1688 (unsigned int) wdr->idx, 1689 hr->http_status, 1690 TALER_ErrorCode_get_hint (hr->ec), 1691 hr->hint); 1692 global_ret = EXIT_FAILURE; 1693 } 1694 GNUNET_CONTAINER_DLL_remove (wdr_head, 1695 wdr_tail, 1696 wdr); 1697 GNUNET_free (wdr); 1698 test_shutdown (); 1699 } 1700 1701 1702 /** 1703 * Upload wire del data. 1704 * 1705 * @param exchange_url base URL of the exchange 1706 * @param idx index of the operation we are performing (for logging) 1707 * @param value arguments for denomination revocation 1708 */ 1709 static void 1710 upload_wire_del (const char *exchange_url, 1711 size_t idx, 1712 const json_t *value) 1713 { 1714 struct TALER_MasterSignatureP master_sig; 1715 struct TALER_FullPayto payto_uri; 1716 struct GNUNET_TIME_Timestamp end_time; 1717 struct WireDelRequest *wdr; 1718 const char *err_name; 1719 unsigned int err_line; 1720 struct GNUNET_JSON_Specification spec[] = { 1721 TALER_JSON_spec_full_payto_uri ("payto_uri", 1722 &payto_uri), 1723 GNUNET_JSON_spec_timestamp ("validity_end", 1724 &end_time), 1725 GNUNET_JSON_spec_fixed_auto ("master_sig", 1726 &master_sig), 1727 GNUNET_JSON_spec_end () 1728 }; 1729 1730 if (GNUNET_OK != 1731 GNUNET_JSON_parse (value, 1732 spec, 1733 &err_name, 1734 &err_line)) 1735 { 1736 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1737 "Invalid input to disable wire account: %s#%u at %u (skipping)\n", 1738 err_name, 1739 err_line, 1740 (unsigned int) idx); 1741 json_dumpf (value, 1742 stderr, 1743 JSON_INDENT (2)); 1744 global_ret = EXIT_FAILURE; 1745 GNUNET_SCHEDULER_shutdown (); 1746 return; 1747 } 1748 wdr = GNUNET_new (struct WireDelRequest); 1749 wdr->idx = idx; 1750 wdr->h = 1751 TALER_EXCHANGE_post_management_wire_disable_create (ctx, 1752 exchange_url, 1753 payto_uri, 1754 end_time, 1755 &master_sig); 1756 TALER_EXCHANGE_post_management_wire_disable_start (wdr->h, 1757 &wire_del_cb, 1758 wdr); 1759 GNUNET_CONTAINER_DLL_insert (wdr_head, 1760 wdr_tail, 1761 wdr); 1762 } 1763 1764 1765 /** 1766 * Function called with information about the post wire fee operation result. 1767 * 1768 * @param wfr the wire fee request 1769 * @param swr response data 1770 */ 1771 static void 1772 wire_fee_cb ( 1773 struct WireFeeRequest *wfr, 1774 const struct TALER_EXCHANGE_PostManagementWireFeesResponse *swr) 1775 { 1776 const struct TALER_EXCHANGE_HttpResponse *hr = &swr->hr; 1777 1778 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1779 { 1780 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1781 "Upload failed for command %u with status %u: %s (%s)\n", 1782 (unsigned int) wfr->idx, 1783 hr->http_status, 1784 TALER_ErrorCode_get_hint (hr->ec), 1785 hr->hint); 1786 global_ret = EXIT_FAILURE; 1787 } 1788 GNUNET_CONTAINER_DLL_remove (wfr_head, 1789 wfr_tail, 1790 wfr); 1791 GNUNET_free (wfr); 1792 test_shutdown (); 1793 } 1794 1795 1796 /** 1797 * Upload wire fee. 1798 * 1799 * @param exchange_url base URL of the exchange 1800 * @param idx index of the operation we are performing (for logging) 1801 * @param value arguments for denomination revocation 1802 */ 1803 static void 1804 upload_wire_fee (const char *exchange_url, 1805 size_t idx, 1806 const json_t *value) 1807 { 1808 struct TALER_MasterSignatureP master_sig; 1809 const char *wire_method; 1810 struct WireFeeRequest *wfr; 1811 const char *err_name; 1812 unsigned int err_line; 1813 struct TALER_WireFeeSet fees; 1814 struct GNUNET_TIME_Timestamp start_time; 1815 struct GNUNET_TIME_Timestamp end_time; 1816 struct GNUNET_JSON_Specification spec[] = { 1817 GNUNET_JSON_spec_string ("wire_method", 1818 &wire_method), 1819 TALER_JSON_spec_amount ("wire_fee", 1820 currency, 1821 &fees.wire), 1822 TALER_JSON_spec_amount ("closing_fee", 1823 currency, 1824 &fees.closing), 1825 GNUNET_JSON_spec_timestamp ("start_time", 1826 &start_time), 1827 GNUNET_JSON_spec_timestamp ("end_time", 1828 &end_time), 1829 GNUNET_JSON_spec_fixed_auto ("master_sig", 1830 &master_sig), 1831 GNUNET_JSON_spec_end () 1832 }; 1833 1834 if (GNUNET_OK != 1835 GNUNET_JSON_parse (value, 1836 spec, 1837 &err_name, 1838 &err_line)) 1839 { 1840 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1841 "Invalid input to set wire fee: %s#%u at %u (skipping)\n", 1842 err_name, 1843 err_line, 1844 (unsigned int) idx); 1845 json_dumpf (value, 1846 stderr, 1847 JSON_INDENT (2)); 1848 global_ret = EXIT_FAILURE; 1849 GNUNET_SCHEDULER_shutdown (); 1850 return; 1851 } 1852 wfr = GNUNET_new (struct WireFeeRequest); 1853 wfr->idx = idx; 1854 wfr->h = 1855 TALER_EXCHANGE_post_management_wire_fees_create (ctx, 1856 exchange_url, 1857 wire_method, 1858 start_time, 1859 end_time, 1860 &fees, 1861 &master_sig); 1862 TALER_EXCHANGE_post_management_wire_fees_start (wfr->h, 1863 &wire_fee_cb, 1864 wfr); 1865 GNUNET_CONTAINER_DLL_insert (wfr_head, 1866 wfr_tail, 1867 wfr); 1868 } 1869 1870 1871 /** 1872 * Function called with information about the post global fee operation result. 1873 * 1874 * @param gfr the global fee request 1875 * @param gr response data 1876 */ 1877 static void 1878 global_fee_cb ( 1879 struct GlobalFeeRequest *gfr, 1880 const struct TALER_EXCHANGE_PostManagementGlobalFeesResponse *gr) 1881 { 1882 const struct TALER_EXCHANGE_HttpResponse *hr = &gr->hr; 1883 1884 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1885 { 1886 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1887 "Upload failed for command %u with status %u: %s (%s)\n", 1888 (unsigned int) gfr->idx, 1889 hr->http_status, 1890 TALER_ErrorCode_get_hint (hr->ec), 1891 hr->hint); 1892 global_ret = EXIT_FAILURE; 1893 } 1894 GNUNET_CONTAINER_DLL_remove (gfr_head, 1895 gfr_tail, 1896 gfr); 1897 GNUNET_free (gfr); 1898 test_shutdown (); 1899 } 1900 1901 1902 /** 1903 * Upload global fee. 1904 * 1905 * @param exchange_url base URL of the exchange 1906 * @param idx index of the operation we are performing (for logging) 1907 * @param value arguments for denomination revocation 1908 */ 1909 static void 1910 upload_global_fee (const char *exchange_url, 1911 size_t idx, 1912 const json_t *value) 1913 { 1914 struct TALER_MasterSignatureP master_sig; 1915 struct GlobalFeeRequest *gfr; 1916 const char *err_name; 1917 unsigned int err_line; 1918 struct TALER_GlobalFeeSet fees; 1919 struct GNUNET_TIME_Timestamp start_time; 1920 struct GNUNET_TIME_Timestamp end_time; 1921 struct GNUNET_TIME_Relative purse_timeout; 1922 struct GNUNET_TIME_Relative history_expiration; 1923 uint32_t purse_account_limit; 1924 struct GNUNET_JSON_Specification spec[] = { 1925 TALER_JSON_spec_amount ("history_fee", 1926 currency, 1927 &fees.history), 1928 TALER_JSON_spec_amount ("account_fee", 1929 currency, 1930 &fees.account), 1931 TALER_JSON_spec_amount ("purse_fee", 1932 currency, 1933 &fees.purse), 1934 GNUNET_JSON_spec_relative_time ("purse_timeout", 1935 &purse_timeout), 1936 GNUNET_JSON_spec_relative_time ("history_expiration", 1937 &history_expiration), 1938 GNUNET_JSON_spec_uint32 ("purse_account_limit", 1939 &purse_account_limit), 1940 GNUNET_JSON_spec_timestamp ("start_time", 1941 &start_time), 1942 GNUNET_JSON_spec_timestamp ("end_time", 1943 &end_time), 1944 GNUNET_JSON_spec_fixed_auto ("master_sig", 1945 &master_sig), 1946 GNUNET_JSON_spec_end () 1947 }; 1948 1949 if (GNUNET_OK != 1950 GNUNET_JSON_parse (value, 1951 spec, 1952 &err_name, 1953 &err_line)) 1954 { 1955 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1956 "Invalid input to set wire fee: %s#%u at %u (skipping)\n", 1957 err_name, 1958 err_line, 1959 (unsigned int) idx); 1960 json_dumpf (value, 1961 stderr, 1962 JSON_INDENT (2)); 1963 global_ret = EXIT_FAILURE; 1964 GNUNET_SCHEDULER_shutdown (); 1965 return; 1966 } 1967 gfr = GNUNET_new (struct GlobalFeeRequest); 1968 gfr->idx = idx; 1969 gfr->h = 1970 TALER_EXCHANGE_post_management_global_fees_create (ctx, 1971 exchange_url, 1972 start_time, 1973 end_time, 1974 &fees, 1975 purse_timeout, 1976 history_expiration, 1977 purse_account_limit, 1978 &master_sig); 1979 TALER_EXCHANGE_post_management_global_fees_start (gfr->h, 1980 &global_fee_cb, 1981 gfr); 1982 GNUNET_CONTAINER_DLL_insert (gfr_head, 1983 gfr_tail, 1984 gfr); 1985 } 1986 1987 1988 /** 1989 * Function called with information about the drain profits operation. 1990 * 1991 * @param dpr the drain profits request 1992 * @param mdr response data 1993 */ 1994 static void 1995 drain_profits_cb ( 1996 struct DrainProfitsRequest *dpr, 1997 const struct TALER_EXCHANGE_PostManagementDrainResponse *mdr) 1998 { 1999 const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr; 2000 2001 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2002 { 2003 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2004 "Upload failed for command %u with status %u: %s (%s)\n", 2005 (unsigned int) dpr->idx, 2006 hr->http_status, 2007 TALER_ErrorCode_get_hint (hr->ec), 2008 hr->hint); 2009 global_ret = EXIT_FAILURE; 2010 } 2011 GNUNET_CONTAINER_DLL_remove (dpr_head, 2012 dpr_tail, 2013 dpr); 2014 GNUNET_free (dpr); 2015 test_shutdown (); 2016 } 2017 2018 2019 /** 2020 * Upload drain profit action. 2021 * 2022 * @param exchange_url base URL of the exchange 2023 * @param idx index of the operation we are performing (for logging) 2024 * @param value arguments for drain profits 2025 */ 2026 static void 2027 upload_drain (const char *exchange_url, 2028 size_t idx, 2029 const json_t *value) 2030 { 2031 struct TALER_WireTransferIdentifierRawP wtid; 2032 struct TALER_MasterSignatureP master_sig; 2033 const char *err_name; 2034 unsigned int err_line; 2035 struct TALER_Amount amount; 2036 struct GNUNET_TIME_Timestamp date; 2037 struct TALER_FullPayto payto_uri; 2038 const char *account_section; 2039 struct DrainProfitsRequest *dpr; 2040 struct GNUNET_JSON_Specification spec[] = { 2041 GNUNET_JSON_spec_fixed_auto ("wtid", 2042 &wtid), 2043 TALER_JSON_spec_amount ("amount", 2044 currency, 2045 &amount), 2046 GNUNET_JSON_spec_timestamp ("date", 2047 &date), 2048 GNUNET_JSON_spec_string ("account_section", 2049 &account_section), 2050 TALER_JSON_spec_full_payto_uri ("payto_uri", 2051 &payto_uri), 2052 GNUNET_JSON_spec_fixed_auto ("master_sig", 2053 &master_sig), 2054 GNUNET_JSON_spec_end () 2055 }; 2056 2057 if (GNUNET_OK != 2058 GNUNET_JSON_parse (value, 2059 spec, 2060 &err_name, 2061 &err_line)) 2062 { 2063 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2064 "Invalid input to drain profits: %s#%u at %u (skipping)\n", 2065 err_name, 2066 err_line, 2067 (unsigned int) idx); 2068 json_dumpf (value, 2069 stderr, 2070 JSON_INDENT (2)); 2071 global_ret = EXIT_FAILURE; 2072 GNUNET_SCHEDULER_shutdown (); 2073 return; 2074 } 2075 dpr = GNUNET_new (struct DrainProfitsRequest); 2076 dpr->idx = idx; 2077 dpr->h = 2078 TALER_EXCHANGE_post_management_drain_create (ctx, 2079 exchange_url, 2080 &wtid, 2081 &amount, 2082 date, 2083 account_section, 2084 payto_uri, 2085 &master_sig); 2086 TALER_EXCHANGE_post_management_drain_start (dpr->h, 2087 &drain_profits_cb, 2088 dpr); 2089 GNUNET_CONTAINER_DLL_insert (dpr_head, 2090 dpr_tail, 2091 dpr); 2092 } 2093 2094 2095 /** 2096 * Function called with information about the post upload keys operation result. 2097 * 2098 * @param ukr the upload keys request 2099 * @param mr response data 2100 */ 2101 static void 2102 keys_cb ( 2103 struct UploadKeysRequest *ukr, 2104 const struct TALER_EXCHANGE_PostManagementKeysResponse *mr) 2105 { 2106 const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr; 2107 2108 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2109 { 2110 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2111 "Upload failed for command %u with status %u: %s (%s)\n", 2112 (unsigned int) ukr->idx, 2113 hr->http_status, 2114 TALER_ErrorCode_get_hint (hr->ec), 2115 hr->hint); 2116 global_ret = EXIT_FAILURE; 2117 } 2118 GNUNET_CONTAINER_DLL_remove (ukr_head, 2119 ukr_tail, 2120 ukr); 2121 GNUNET_free (ukr); 2122 test_shutdown (); 2123 } 2124 2125 2126 /** 2127 * Upload (denomination and signing) key master signatures. 2128 * 2129 * @param exchange_url base URL of the exchange 2130 * @param idx index of the operation we are performing (for logging) 2131 * @param value arguments for POSTing keys 2132 */ 2133 static void 2134 upload_keys (const char *exchange_url, 2135 size_t idx, 2136 const json_t *value) 2137 { 2138 struct TALER_EXCHANGE_ManagementPostKeysData pkd; 2139 struct UploadKeysRequest *ukr; 2140 const char *err_name; 2141 unsigned int err_line; 2142 const json_t *denom_sigs; 2143 const json_t *signkey_sigs; 2144 struct GNUNET_JSON_Specification spec[] = { 2145 GNUNET_JSON_spec_array_const ("denom_sigs", 2146 &denom_sigs), 2147 GNUNET_JSON_spec_array_const ("signkey_sigs", 2148 &signkey_sigs), 2149 GNUNET_JSON_spec_end () 2150 }; 2151 bool ok = true; 2152 2153 if (GNUNET_OK != 2154 GNUNET_JSON_parse (value, 2155 spec, 2156 &err_name, 2157 &err_line)) 2158 { 2159 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2160 "Invalid input to 'upload': %s#%u (skipping)\n", 2161 err_name, 2162 err_line); 2163 json_dumpf (value, 2164 stderr, 2165 JSON_INDENT (2)); 2166 global_ret = EXIT_FAILURE; 2167 GNUNET_SCHEDULER_shutdown (); 2168 return; 2169 } 2170 pkd.num_sign_sigs = json_array_size (signkey_sigs); 2171 pkd.num_denom_sigs = json_array_size (denom_sigs); 2172 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2173 "Uploading %u denomination and %u signing key signatures\n", 2174 pkd.num_denom_sigs, 2175 pkd.num_sign_sigs); 2176 pkd.sign_sigs = GNUNET_new_array ( 2177 pkd.num_sign_sigs, 2178 struct TALER_EXCHANGE_SigningKeySignature); 2179 pkd.denom_sigs = GNUNET_new_array ( 2180 pkd.num_denom_sigs, 2181 struct TALER_EXCHANGE_DenominationKeySignature); 2182 for (unsigned int i = 0; i<pkd.num_sign_sigs; i++) 2183 { 2184 struct TALER_EXCHANGE_SigningKeySignature *ss = &pkd.sign_sigs[i]; 2185 json_t *val = json_array_get (signkey_sigs, 2186 i); 2187 struct GNUNET_JSON_Specification ispec[] = { 2188 GNUNET_JSON_spec_fixed_auto ("exchange_pub", 2189 &ss->exchange_pub), 2190 GNUNET_JSON_spec_fixed_auto ("master_sig", 2191 &ss->master_sig), 2192 GNUNET_JSON_spec_end () 2193 }; 2194 2195 if (GNUNET_OK != 2196 GNUNET_JSON_parse (val, 2197 ispec, 2198 &err_name, 2199 &err_line)) 2200 { 2201 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2202 "Invalid input for signkey validity: %s#%u at %u (aborting)\n", 2203 err_name, 2204 err_line, 2205 i); 2206 json_dumpf (val, 2207 stderr, 2208 JSON_INDENT (2)); 2209 ok = false; 2210 } 2211 } 2212 for (unsigned int i = 0; i<pkd.num_denom_sigs; i++) 2213 { 2214 struct TALER_EXCHANGE_DenominationKeySignature *ds = &pkd.denom_sigs[i]; 2215 json_t *val = json_array_get (denom_sigs, 2216 i); 2217 struct GNUNET_JSON_Specification ispec[] = { 2218 GNUNET_JSON_spec_fixed_auto ("h_denom_pub", 2219 &ds->h_denom_pub), 2220 GNUNET_JSON_spec_fixed_auto ("master_sig", 2221 &ds->master_sig), 2222 GNUNET_JSON_spec_end () 2223 }; 2224 2225 if (GNUNET_OK != 2226 GNUNET_JSON_parse (val, 2227 ispec, 2228 &err_name, 2229 &err_line)) 2230 { 2231 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2232 "Invalid input for denomination validity: %s#%u at %u (aborting)\n", 2233 err_name, 2234 err_line, 2235 i); 2236 json_dumpf (val, 2237 stderr, 2238 JSON_INDENT (2)); 2239 ok = false; 2240 } 2241 } 2242 2243 if (ok) 2244 { 2245 ukr = GNUNET_new (struct UploadKeysRequest); 2246 ukr->idx = idx; 2247 ukr->h = 2248 TALER_EXCHANGE_post_management_keys_create (ctx, 2249 exchange_url, 2250 &pkd); 2251 TALER_EXCHANGE_post_management_keys_start (ukr->h, 2252 &keys_cb, 2253 ukr); 2254 GNUNET_CONTAINER_DLL_insert (ukr_head, 2255 ukr_tail, 2256 ukr); 2257 } 2258 else 2259 { 2260 global_ret = EXIT_FAILURE; 2261 GNUNET_SCHEDULER_shutdown (); 2262 } 2263 GNUNET_free (pkd.sign_sigs); 2264 GNUNET_free (pkd.denom_sigs); 2265 } 2266 2267 2268 /** 2269 * Function called with information about the add partner operation. 2270 * 2271 * @param par the request 2272 * @param apr response data 2273 */ 2274 static void 2275 add_partner_cb ( 2276 struct PartnerAddRequest *par, 2277 const struct TALER_EXCHANGE_PostManagementPartnersResponse *apr) 2278 { 2279 const struct TALER_EXCHANGE_HttpResponse *hr = &apr->hr; 2280 2281 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2282 { 2283 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2284 "Upload failed for command %u with status %u: %s (%s)\n", 2285 (unsigned int) par->idx, 2286 hr->http_status, 2287 TALER_ErrorCode_get_hint (hr->ec), 2288 hr->hint); 2289 global_ret = EXIT_FAILURE; 2290 } 2291 GNUNET_CONTAINER_DLL_remove (par_head, 2292 par_tail, 2293 par); 2294 GNUNET_free (par); 2295 test_shutdown (); 2296 } 2297 2298 2299 /** 2300 * Add partner action. 2301 * 2302 * @param exchange_url base URL of the exchange 2303 * @param idx index of the operation we are performing (for logging) 2304 * @param value arguments for add partner 2305 */ 2306 static void 2307 add_partner (const char *exchange_url, 2308 size_t idx, 2309 const json_t *value) 2310 { 2311 struct TALER_MasterPublicKeyP partner_pub; 2312 struct GNUNET_TIME_Timestamp start_date; 2313 struct GNUNET_TIME_Timestamp end_date; 2314 struct GNUNET_TIME_Relative wad_frequency; 2315 struct TALER_Amount wad_fee; 2316 const char *partner_base_url; 2317 struct TALER_MasterSignatureP master_sig; 2318 struct PartnerAddRequest *par; 2319 struct GNUNET_JSON_Specification spec[] = { 2320 GNUNET_JSON_spec_fixed_auto ("partner_pub", 2321 &partner_pub), 2322 TALER_JSON_spec_amount ("wad_fee", 2323 currency, 2324 &wad_fee), 2325 GNUNET_JSON_spec_relative_time ("wad_frequency", 2326 &wad_frequency), 2327 GNUNET_JSON_spec_timestamp ("start_date", 2328 &start_date), 2329 GNUNET_JSON_spec_timestamp ("end_date", 2330 &end_date), 2331 TALER_JSON_spec_web_url ("partner_base_url", 2332 &partner_base_url), 2333 GNUNET_JSON_spec_fixed_auto ("master_sig", 2334 &master_sig), 2335 GNUNET_JSON_spec_end () 2336 }; 2337 const char *err_name; 2338 unsigned int err_line; 2339 2340 if (GNUNET_OK != 2341 GNUNET_JSON_parse (value, 2342 spec, 2343 &err_name, 2344 &err_line)) 2345 { 2346 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2347 "Invalid input to add partner: %s#%u at %u (skipping)\n", 2348 err_name, 2349 err_line, 2350 (unsigned int) idx); 2351 json_dumpf (value, 2352 stderr, 2353 JSON_INDENT (2)); 2354 global_ret = EXIT_FAILURE; 2355 GNUNET_SCHEDULER_shutdown (); 2356 return; 2357 } 2358 par = GNUNET_new (struct PartnerAddRequest); 2359 par->idx = idx; 2360 par->h = 2361 TALER_EXCHANGE_post_management_partners_create (ctx, 2362 exchange_url, 2363 &partner_pub, 2364 start_date, 2365 end_date, 2366 wad_frequency, 2367 &wad_fee, 2368 partner_base_url, 2369 &master_sig); 2370 TALER_EXCHANGE_post_management_partners_start (par->h, 2371 &add_partner_cb, 2372 par); 2373 GNUNET_CONTAINER_DLL_insert (par_head, 2374 par_tail, 2375 par); 2376 } 2377 2378 2379 /** 2380 * Function called with information about the AML officer update operation. 2381 * 2382 * @param asr the request 2383 * @param ar response data 2384 */ 2385 static void 2386 update_aml_officer_cb ( 2387 TALER_EXCHANGE_POST_MANAGEMENT_AML_OFFICERS_RESULT_CLOSURE *asr, 2388 const struct TALER_EXCHANGE_PostManagementAmlOfficersResponse *ar) 2389 { 2390 const struct TALER_EXCHANGE_HttpResponse *hr = &ar->hr; 2391 2392 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2393 { 2394 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2395 "Upload failed for command %u with status %u: %s (%s)\n", 2396 (unsigned int) asr->idx, 2397 hr->http_status, 2398 TALER_ErrorCode_get_hint (hr->ec), 2399 hr->hint); 2400 global_ret = EXIT_FAILURE; 2401 } 2402 GNUNET_CONTAINER_DLL_remove (asr_head, 2403 asr_tail, 2404 asr); 2405 GNUNET_free (asr); 2406 test_shutdown (); 2407 } 2408 2409 2410 /** 2411 * Upload AML staff action. 2412 * 2413 * @param exchange_url base URL of the exchange 2414 * @param idx index of the operation we are performing (for logging) 2415 * @param value arguments for AML staff change 2416 */ 2417 static void 2418 update_aml_staff (const char *exchange_url, 2419 size_t idx, 2420 const json_t *value) 2421 { 2422 struct TALER_AmlOfficerPublicKeyP officer_pub; 2423 const char *officer_name; 2424 struct GNUNET_TIME_Timestamp change_date; 2425 bool is_active; 2426 bool read_only; 2427 struct TALER_MasterSignatureP master_sig; 2428 struct AmlStaffRequest *asr; 2429 struct GNUNET_JSON_Specification spec[] = { 2430 GNUNET_JSON_spec_fixed_auto ("officer_pub", 2431 &officer_pub), 2432 GNUNET_JSON_spec_timestamp ("change_date", 2433 &change_date), 2434 GNUNET_JSON_spec_bool ("is_active", 2435 &is_active), 2436 GNUNET_JSON_spec_bool ("read_only", 2437 &read_only), 2438 GNUNET_JSON_spec_string ("officer_name", 2439 &officer_name), 2440 GNUNET_JSON_spec_fixed_auto ("master_sig", 2441 &master_sig), 2442 GNUNET_JSON_spec_end () 2443 }; 2444 const char *err_name; 2445 unsigned int err_line; 2446 2447 if (GNUNET_OK != 2448 GNUNET_JSON_parse (value, 2449 spec, 2450 &err_name, 2451 &err_line)) 2452 { 2453 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2454 "Invalid input to AML staff update: %s#%u at %u (skipping)\n", 2455 err_name, 2456 err_line, 2457 (unsigned int) idx); 2458 json_dumpf (value, 2459 stderr, 2460 JSON_INDENT (2)); 2461 global_ret = EXIT_FAILURE; 2462 GNUNET_SCHEDULER_shutdown (); 2463 return; 2464 } 2465 asr = GNUNET_new (struct AmlStaffRequest); 2466 asr->idx = idx; 2467 asr->h = TALER_EXCHANGE_post_management_aml_officers_create ( 2468 ctx, 2469 exchange_url, 2470 &officer_pub, 2471 officer_name, 2472 change_date, 2473 is_active, 2474 read_only, 2475 &master_sig); 2476 GNUNET_CONTAINER_DLL_insert (asr_head, 2477 asr_tail, 2478 asr); 2479 GNUNET_assert (TALER_EC_NONE == 2480 TALER_EXCHANGE_post_management_aml_officers_start ( 2481 asr->h, 2482 &update_aml_officer_cb, 2483 asr)); 2484 } 2485 2486 2487 /** 2488 * Perform uploads based on the JSON in #out. 2489 * 2490 * @param exchange_url base URL of the exchange to use 2491 */ 2492 static void 2493 trigger_upload (const char *exchange_url) 2494 { 2495 struct UploadHandler uhs[] = { 2496 { 2497 .key = OP_REVOKE_DENOMINATION, 2498 .cb = &upload_denom_revocation 2499 }, 2500 { 2501 .key = OP_REVOKE_SIGNKEY, 2502 .cb = &upload_signkey_revocation 2503 }, 2504 { 2505 .key = OP_ENABLE_AUDITOR, 2506 .cb = &upload_auditor_add 2507 }, 2508 { 2509 .key = OP_DISABLE_AUDITOR, 2510 .cb = &upload_auditor_del 2511 }, 2512 { 2513 .key = OP_ENABLE_WIRE, 2514 .cb = &upload_wire_add 2515 }, 2516 { 2517 .key = OP_DISABLE_WIRE, 2518 .cb = &upload_wire_del 2519 }, 2520 { 2521 .key = OP_SET_WIRE_FEE, 2522 .cb = &upload_wire_fee 2523 }, 2524 { 2525 .key = OP_SET_GLOBAL_FEE, 2526 .cb = &upload_global_fee 2527 }, 2528 { 2529 .key = OP_UPLOAD_SIGS, 2530 .cb = &upload_keys 2531 }, 2532 { 2533 .key = OP_DRAIN_PROFITS, 2534 .cb = &upload_drain 2535 }, 2536 { 2537 .key = OP_UPDATE_AML_STAFF, 2538 .cb = &update_aml_staff 2539 }, 2540 { 2541 .key = OP_ADD_PARTNER, 2542 .cb = &add_partner 2543 }, 2544 /* array termination */ 2545 { 2546 .key = NULL 2547 } 2548 }; 2549 size_t index; 2550 json_t *obj; 2551 2552 json_array_foreach (out, index, obj) { 2553 bool found = false; 2554 const char *key; 2555 const json_t *value; 2556 2557 key = json_string_value (json_object_get (obj, "operation")); 2558 value = json_object_get (obj, "arguments"); 2559 if (NULL == key) 2560 { 2561 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2562 "Malformed JSON input\n"); 2563 global_ret = EXIT_FAILURE; 2564 GNUNET_SCHEDULER_shutdown (); 2565 return; 2566 } 2567 /* block of code that uses key and value */ 2568 for (unsigned int i = 0; NULL != uhs[i].key; i++) 2569 { 2570 if (0 == strcasecmp (key, 2571 uhs[i].key)) 2572 { 2573 2574 found = true; 2575 uhs[i].cb (exchange_url, 2576 index, 2577 value); 2578 break; 2579 } 2580 } 2581 if (! found) 2582 { 2583 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2584 "Upload does not know how to handle `%s'\n", 2585 key); 2586 global_ret = EXIT_FAILURE; 2587 GNUNET_SCHEDULER_shutdown (); 2588 return; 2589 } 2590 } 2591 } 2592 2593 2594 /** 2595 * Upload operation result (signatures) to exchange. 2596 * 2597 * @param args the array of command-line arguments to process next 2598 */ 2599 static void 2600 do_upload (char *const *args) 2601 { 2602 (void) args; 2603 if (NULL != in) 2604 { 2605 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2606 "Downloaded data was not consumed, refusing upload\n"); 2607 GNUNET_SCHEDULER_shutdown (); 2608 global_ret = EXIT_FAILURE; 2609 return; 2610 } 2611 if (NULL == out) 2612 { 2613 json_error_t err; 2614 2615 out = json_loadf (stdin, 2616 JSON_REJECT_DUPLICATES, 2617 &err); 2618 if (NULL == out) 2619 { 2620 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2621 "Failed to read JSON input: %s at %d:%s (offset: %d)\n", 2622 err.text, 2623 err.line, 2624 err.source, 2625 err.position); 2626 GNUNET_SCHEDULER_shutdown (); 2627 global_ret = EXIT_FAILURE; 2628 return; 2629 } 2630 } 2631 if (! json_is_array (out)) 2632 { 2633 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2634 "Error: expected JSON array for `upload` command\n"); 2635 GNUNET_SCHEDULER_shutdown (); 2636 global_ret = EXIT_FAILURE; 2637 return; 2638 } 2639 if ( (NULL == CFG_exchange_url) && 2640 (GNUNET_OK != 2641 GNUNET_CONFIGURATION_get_value_string (kcfg, 2642 "exchange", 2643 "BASE_URL", 2644 &CFG_exchange_url)) ) 2645 { 2646 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2647 "exchange", 2648 "BASE_URL"); 2649 global_ret = EXIT_NOTCONFIGURED; 2650 GNUNET_SCHEDULER_shutdown (); 2651 return; 2652 } 2653 trigger_upload (CFG_exchange_url); 2654 json_decref (out); 2655 out = NULL; 2656 } 2657 2658 2659 /** 2660 * Revoke denomination key. 2661 * 2662 * @param args the array of command-line arguments to process next; 2663 * args[0] must be the hash of the denomination key to revoke 2664 */ 2665 static void 2666 do_revoke_denomination_key (char *const *args) 2667 { 2668 struct TALER_DenominationHashP h_denom_pub; 2669 struct TALER_MasterSignatureP master_sig; 2670 2671 if (NULL != in) 2672 { 2673 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2674 "Downloaded data was not consumed, refusing revocation\n"); 2675 GNUNET_SCHEDULER_shutdown (); 2676 global_ret = EXIT_FAILURE; 2677 return; 2678 } 2679 if ( (NULL == args[0]) || 2680 (GNUNET_OK != 2681 GNUNET_STRINGS_string_to_data (args[0], 2682 strlen (args[0]), 2683 &h_denom_pub, 2684 sizeof (h_denom_pub))) ) 2685 { 2686 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2687 "You must specify a denomination key with this subcommand\n"); 2688 GNUNET_SCHEDULER_shutdown (); 2689 global_ret = EXIT_INVALIDARGUMENT; 2690 return; 2691 } 2692 if (GNUNET_OK != 2693 load_offline_key (GNUNET_NO)) 2694 return; 2695 TALER_exchange_offline_denomination_revoke_sign (&h_denom_pub, 2696 &master_priv, 2697 &master_sig); 2698 output_operation (OP_REVOKE_DENOMINATION, 2699 GNUNET_JSON_PACK ( 2700 GNUNET_JSON_pack_data_auto ("h_denom_pub", 2701 &h_denom_pub), 2702 GNUNET_JSON_pack_data_auto ("master_sig", 2703 &master_sig))); 2704 next (args + 1); 2705 } 2706 2707 2708 /** 2709 * Revoke signkey. 2710 * 2711 * @param args the array of command-line arguments to process next; 2712 * args[0] must be the hash of the denomination key to revoke 2713 */ 2714 static void 2715 do_revoke_signkey (char *const *args) 2716 { 2717 struct TALER_ExchangePublicKeyP exchange_pub; 2718 struct TALER_MasterSignatureP master_sig; 2719 2720 if (NULL != in) 2721 { 2722 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2723 "Downloaded data was not consumed, refusing revocation\n"); 2724 GNUNET_SCHEDULER_shutdown (); 2725 global_ret = EXIT_FAILURE; 2726 return; 2727 } 2728 if ( (NULL == args[0]) || 2729 (GNUNET_OK != 2730 GNUNET_STRINGS_string_to_data (args[0], 2731 strlen (args[0]), 2732 &exchange_pub, 2733 sizeof (exchange_pub))) ) 2734 { 2735 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2736 "You must specify an exchange signing key with this subcommand\n"); 2737 GNUNET_SCHEDULER_shutdown (); 2738 global_ret = EXIT_INVALIDARGUMENT; 2739 return; 2740 } 2741 if (GNUNET_OK != 2742 load_offline_key (GNUNET_NO)) 2743 return; 2744 TALER_exchange_offline_signkey_revoke_sign (&exchange_pub, 2745 &master_priv, 2746 &master_sig); 2747 output_operation (OP_REVOKE_SIGNKEY, 2748 GNUNET_JSON_PACK ( 2749 GNUNET_JSON_pack_data_auto ("exchange_pub", 2750 &exchange_pub), 2751 GNUNET_JSON_pack_data_auto ("master_sig", 2752 &master_sig))); 2753 next (args + 1); 2754 } 2755 2756 2757 /** 2758 * Add auditor. 2759 * 2760 * @param args the array of command-line arguments to process next; 2761 * args[0] must be the auditor's public key, args[1] the auditor's 2762 * API base URL, and args[2] the auditor's name. 2763 */ 2764 static void 2765 do_add_auditor (char *const *args) 2766 { 2767 struct TALER_MasterSignatureP master_sig; 2768 struct TALER_AuditorPublicKeyP auditor_pub; 2769 struct GNUNET_TIME_Timestamp now; 2770 2771 if (NULL != in) 2772 { 2773 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2774 "Downloaded data was not consumed, not adding auditor\n"); 2775 GNUNET_SCHEDULER_shutdown (); 2776 global_ret = EXIT_FAILURE; 2777 return; 2778 } 2779 if ( (NULL == args[0]) || 2780 (GNUNET_OK != 2781 GNUNET_STRINGS_string_to_data (args[0], 2782 strlen (args[0]), 2783 &auditor_pub, 2784 sizeof (auditor_pub))) ) 2785 { 2786 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2787 "You must specify an auditor public key as first argument for this subcommand\n"); 2788 GNUNET_SCHEDULER_shutdown (); 2789 global_ret = EXIT_INVALIDARGUMENT; 2790 return; 2791 } 2792 2793 if ( (NULL == args[1]) || 2794 (0 != strncmp ("http", 2795 args[1], 2796 strlen ("http"))) || 2797 (NULL == args[2]) ) 2798 { 2799 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2800 "You must specify an auditor URI and auditor name as 2nd and 3rd arguments to this subcommand\n"); 2801 GNUNET_SCHEDULER_shutdown (); 2802 global_ret = EXIT_INVALIDARGUMENT; 2803 return; 2804 } 2805 if (GNUNET_OK != 2806 load_offline_key (GNUNET_NO)) 2807 return; 2808 now = GNUNET_TIME_timestamp_get (); 2809 TALER_exchange_offline_auditor_add_sign (&auditor_pub, 2810 args[1], 2811 now, 2812 &master_priv, 2813 &master_sig); 2814 output_operation (OP_ENABLE_AUDITOR, 2815 GNUNET_JSON_PACK ( 2816 GNUNET_JSON_pack_string ("auditor_url", 2817 args[1]), 2818 GNUNET_JSON_pack_string ("auditor_name", 2819 args[2]), 2820 GNUNET_JSON_pack_timestamp ("validity_start", 2821 now), 2822 GNUNET_JSON_pack_data_auto ("auditor_pub", 2823 &auditor_pub), 2824 GNUNET_JSON_pack_data_auto ("master_sig", 2825 &master_sig))); 2826 next (args + 3); 2827 } 2828 2829 2830 /** 2831 * Disable auditor account. 2832 * 2833 * @param args the array of command-line arguments to process next; 2834 * args[0] must be the hash of the denomination key to revoke 2835 */ 2836 static void 2837 do_del_auditor (char *const *args) 2838 { 2839 struct TALER_MasterSignatureP master_sig; 2840 struct TALER_AuditorPublicKeyP auditor_pub; 2841 struct GNUNET_TIME_Timestamp now; 2842 2843 if (NULL != in) 2844 { 2845 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2846 "Downloaded data was not consumed, not deleting auditor account\n"); 2847 GNUNET_SCHEDULER_shutdown (); 2848 global_ret = EXIT_FAILURE; 2849 return; 2850 } 2851 if ( (NULL == args[0]) || 2852 (GNUNET_OK != 2853 GNUNET_STRINGS_string_to_data (args[0], 2854 strlen (args[0]), 2855 &auditor_pub, 2856 sizeof (auditor_pub))) ) 2857 { 2858 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2859 "You must specify an auditor public key as first argument for this subcommand\n"); 2860 GNUNET_SCHEDULER_shutdown (); 2861 global_ret = EXIT_INVALIDARGUMENT; 2862 return; 2863 } 2864 if (GNUNET_OK != 2865 load_offline_key (GNUNET_NO)) 2866 return; 2867 now = GNUNET_TIME_timestamp_get (); 2868 TALER_exchange_offline_auditor_del_sign (&auditor_pub, 2869 now, 2870 &master_priv, 2871 &master_sig); 2872 output_operation (OP_DISABLE_AUDITOR, 2873 GNUNET_JSON_PACK ( 2874 GNUNET_JSON_pack_data_auto ("auditor_pub", 2875 &auditor_pub), 2876 GNUNET_JSON_pack_timestamp ("validity_end", 2877 now), 2878 GNUNET_JSON_pack_data_auto ("master_sig", 2879 &master_sig))); 2880 next (args + 1); 2881 } 2882 2883 2884 /** 2885 * Parse account restriction. 2886 * 2887 * @param args the array of command-line arguments to process next 2888 * @param[in,out] restrictions JSON array to update 2889 * @return -1 on error, otherwise number of arguments from @a args that were used 2890 */ 2891 static int 2892 parse_restriction (char *const *args, 2893 json_t *restrictions) 2894 { 2895 if (NULL == args[0]) 2896 { 2897 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2898 "Restriction TYPE argument missing\n"); 2899 return -1; 2900 } 2901 if (0 == strcmp (args[0], 2902 "deny")) 2903 { 2904 GNUNET_assert (0 == 2905 json_array_append_new ( 2906 restrictions, 2907 GNUNET_JSON_PACK ( 2908 GNUNET_JSON_pack_string ("type", 2909 "deny")))); 2910 return 1; 2911 } 2912 if (0 == strcmp (args[0], 2913 "regex")) 2914 { 2915 json_t *i18n; 2916 json_error_t err; 2917 2918 if ( (NULL == args[1]) || 2919 (NULL == args[2]) || 2920 (NULL == args[3]) ) 2921 { 2922 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2923 "Mandatory arguments for restriction of type `regex' missing (REGEX, HINT, HINT-I18 required)\n"); 2924 return -1; 2925 } 2926 { 2927 regex_t ex; 2928 2929 if (0 != regcomp (&ex, 2930 args[1], 2931 REG_NOSUB | REG_EXTENDED)) 2932 { 2933 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2934 "Invalid regular expression `%s'\n", 2935 args[1]); 2936 return -1; 2937 } 2938 regfree (&ex); 2939 } 2940 2941 i18n = json_loads (args[3], 2942 JSON_REJECT_DUPLICATES, 2943 &err); 2944 if (NULL == i18n) 2945 { 2946 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2947 "Invalid JSON for restriction of type `regex': `%s` at %d\n", 2948 args[3], 2949 err.position); 2950 return -1; 2951 } 2952 GNUNET_assert (0 == 2953 json_array_append_new ( 2954 restrictions, 2955 GNUNET_JSON_PACK ( 2956 GNUNET_JSON_pack_string ("type", 2957 "regex"), 2958 GNUNET_JSON_pack_string ("payto_regex", 2959 args[1]), 2960 GNUNET_JSON_pack_string ("human_hint", 2961 args[2]), 2962 GNUNET_JSON_pack_object_steal ("human_hint_i18n", 2963 i18n) 2964 ))); 2965 return 4; 2966 } 2967 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2968 "Restriction TYPE `%s' unsupported\n", 2969 args[0]); 2970 return -1; 2971 } 2972 2973 2974 /** 2975 * Add wire account. 2976 * 2977 * @param args the array of command-line arguments to process next; 2978 * args[0] must be the hash of the denomination key to revoke 2979 */ 2980 static void 2981 do_add_wire (char *const *args) 2982 { 2983 struct TALER_MasterSignatureP master_sig_add; 2984 struct TALER_MasterSignatureP master_sig_wire; 2985 struct GNUNET_TIME_Timestamp now; 2986 const char *conversion_url = NULL; 2987 const char *open_banking_gateway = NULL; 2988 const char *prepared_transfer_url = NULL; 2989 const char *bank_label = NULL; 2990 int64_t priority = 0; 2991 json_t *debit_restrictions; 2992 json_t *credit_restrictions; 2993 unsigned int num_args = 1; 2994 struct TALER_FullPayto payto_uri = { 2995 .full_payto = args[0] 2996 }; 2997 2998 if (NULL != in) 2999 { 3000 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3001 "Downloaded data was not consumed, not adding wire account\n"); 3002 GNUNET_SCHEDULER_shutdown (); 3003 global_ret = EXIT_FAILURE; 3004 return; 3005 } 3006 if (NULL == args[0]) 3007 { 3008 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3009 "You must specify a payto://-URI with this subcommand\n"); 3010 GNUNET_SCHEDULER_shutdown (); 3011 global_ret = EXIT_INVALIDARGUMENT; 3012 return; 3013 } 3014 if (GNUNET_OK != 3015 load_offline_key (GNUNET_NO)) 3016 return; 3017 { 3018 char *msg = TALER_payto_validate (payto_uri); 3019 3020 if (NULL != msg) 3021 { 3022 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3023 "payto URI `%s' is malformed: %s\n", 3024 payto_uri.full_payto, 3025 msg); 3026 GNUNET_free (msg); 3027 GNUNET_SCHEDULER_shutdown (); 3028 global_ret = EXIT_INVALIDARGUMENT; 3029 return; 3030 } 3031 } 3032 now = GNUNET_TIME_timestamp_get (); 3033 { 3034 char *wire_method; 3035 3036 wire_method = TALER_payto_get_method (payto_uri.full_payto); 3037 if (NULL == wire_method) 3038 { 3039 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3040 "payto:// URI `%s' is malformed\n", 3041 payto_uri.full_payto); 3042 global_ret = EXIT_INVALIDARGUMENT; 3043 GNUNET_SCHEDULER_shutdown (); 3044 return; 3045 } 3046 GNUNET_free (wire_method); 3047 } 3048 debit_restrictions = json_array (); 3049 GNUNET_assert (NULL != debit_restrictions); 3050 credit_restrictions = json_array (); 3051 GNUNET_assert (NULL != credit_restrictions); 3052 while (NULL != args[num_args]) 3053 { 3054 if (0 == strcmp (args[num_args], 3055 "conversion-url")) 3056 { 3057 num_args++; 3058 conversion_url = args[num_args]; 3059 if (NULL == conversion_url) 3060 { 3061 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3062 "'conversion-url' requires an argument\n"); 3063 global_ret = EXIT_INVALIDARGUMENT; 3064 GNUNET_SCHEDULER_shutdown (); 3065 json_decref (debit_restrictions); 3066 json_decref (credit_restrictions); 3067 return; 3068 } 3069 if (! TALER_is_web_url (conversion_url)) 3070 { 3071 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3072 "'conversion-url' must refer to HTTP(S) endpoint, `%s' is invalid\n", 3073 conversion_url); 3074 global_ret = EXIT_INVALIDARGUMENT; 3075 GNUNET_SCHEDULER_shutdown (); 3076 json_decref (debit_restrictions); 3077 json_decref (credit_restrictions); 3078 return; 3079 } 3080 num_args++; 3081 continue; 3082 } 3083 3084 if (0 == strcmp (args[num_args], 3085 "open-banking-gateway")) 3086 { 3087 num_args++; 3088 open_banking_gateway = args[num_args]; 3089 if (NULL == open_banking_gateway) 3090 { 3091 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3092 "'open-banking-gateway' requires an argument\n"); 3093 global_ret = EXIT_INVALIDARGUMENT; 3094 GNUNET_SCHEDULER_shutdown (); 3095 json_decref (debit_restrictions); 3096 json_decref (credit_restrictions); 3097 return; 3098 } 3099 if (! TALER_is_web_url (open_banking_gateway)) 3100 { 3101 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3102 "'open-banking-gateway' must refer to HTTP(S) endpoint, `%s' is invalid\n", 3103 open_banking_gateway); 3104 global_ret = EXIT_INVALIDARGUMENT; 3105 GNUNET_SCHEDULER_shutdown (); 3106 json_decref (debit_restrictions); 3107 json_decref (credit_restrictions); 3108 return; 3109 } 3110 num_args++; 3111 continue; 3112 } 3113 3114 if (0 == strcmp (args[num_args], 3115 "prepared-transfer-url")) 3116 { 3117 num_args++; 3118 prepared_transfer_url = args[num_args]; 3119 if (NULL == prepared_transfer_url) 3120 { 3121 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3122 "'prepared-transfer-url' requires an argument\n"); 3123 global_ret = EXIT_INVALIDARGUMENT; 3124 GNUNET_SCHEDULER_shutdown (); 3125 json_decref (debit_restrictions); 3126 json_decref (credit_restrictions); 3127 return; 3128 } 3129 if (! TALER_is_web_url (prepared_transfer_url)) 3130 { 3131 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3132 "'prepared-transfer-url' must refer to HTTP(S) endpoint, `%s' is invalid\n", 3133 prepared_transfer_url); 3134 global_ret = EXIT_INVALIDARGUMENT; 3135 GNUNET_SCHEDULER_shutdown (); 3136 json_decref (debit_restrictions); 3137 json_decref (credit_restrictions); 3138 return; 3139 } 3140 num_args++; 3141 continue; 3142 } 3143 3144 if (0 == strcmp (args[num_args], 3145 "credit-restriction")) 3146 { 3147 int iret; 3148 3149 num_args++; 3150 iret = parse_restriction (&args[num_args], 3151 credit_restrictions); 3152 if (iret <= 0) 3153 { 3154 global_ret = EXIT_INVALIDARGUMENT; 3155 GNUNET_SCHEDULER_shutdown (); 3156 json_decref (debit_restrictions); 3157 json_decref (credit_restrictions); 3158 return; 3159 } 3160 num_args += iret; 3161 continue; 3162 } 3163 if (0 == strcmp (args[num_args], 3164 "debit-restriction")) 3165 { 3166 int iret; 3167 3168 num_args++; 3169 iret = parse_restriction (&args[num_args], 3170 debit_restrictions); 3171 if (iret <= 0) 3172 { 3173 global_ret = EXIT_INVALIDARGUMENT; 3174 GNUNET_SCHEDULER_shutdown (); 3175 json_decref (debit_restrictions); 3176 json_decref (credit_restrictions); 3177 return; 3178 } 3179 num_args += iret; 3180 continue; 3181 } 3182 if (0 == strcmp (args[num_args], 3183 "display-hint")) 3184 { 3185 long long p; 3186 char dummy; 3187 3188 num_args++; 3189 if ( (NULL == args[num_args]) || 3190 (NULL == args[num_args + 1]) ) 3191 { 3192 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3193 "'display-hint' requires at least two arguments (priority and label)\n"); 3194 global_ret = EXIT_INVALIDARGUMENT; 3195 GNUNET_SCHEDULER_shutdown (); 3196 json_decref (debit_restrictions); 3197 json_decref (credit_restrictions); 3198 return; 3199 } 3200 if (1 != sscanf (args[num_args], 3201 "%lld%c", 3202 &p, 3203 &dummy)) 3204 { 3205 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3206 "Priority argument `%s' is not a number\n", 3207 args[num_args]); 3208 global_ret = EXIT_INVALIDARGUMENT; 3209 GNUNET_SCHEDULER_shutdown (); 3210 json_decref (debit_restrictions); 3211 json_decref (credit_restrictions); 3212 return; 3213 } 3214 priority = (int64_t) p; 3215 num_args++; 3216 bank_label = args[num_args]; 3217 num_args++; 3218 continue; 3219 } 3220 break; 3221 } 3222 TALER_exchange_offline_wire_add_sign (payto_uri, 3223 conversion_url, 3224 open_banking_gateway, 3225 prepared_transfer_url, 3226 debit_restrictions, 3227 credit_restrictions, 3228 now, 3229 &master_priv, 3230 &master_sig_add); 3231 TALER_exchange_wire_signature_make (payto_uri, 3232 conversion_url, 3233 open_banking_gateway, 3234 prepared_transfer_url, 3235 debit_restrictions, 3236 credit_restrictions, 3237 &master_priv, 3238 &master_sig_wire); 3239 output_operation (OP_ENABLE_WIRE, 3240 GNUNET_JSON_PACK ( 3241 TALER_JSON_pack_full_payto ("payto_uri", 3242 payto_uri), 3243 GNUNET_JSON_pack_array_steal ("debit_restrictions", 3244 debit_restrictions), 3245 GNUNET_JSON_pack_array_steal ("credit_restrictions", 3246 credit_restrictions), 3247 GNUNET_JSON_pack_allow_null ( 3248 GNUNET_JSON_pack_string ("conversion_url", 3249 conversion_url)), 3250 GNUNET_JSON_pack_allow_null ( 3251 GNUNET_JSON_pack_string ("open_banking_gateway", 3252 open_banking_gateway)), 3253 GNUNET_JSON_pack_allow_null ( 3254 GNUNET_JSON_pack_string ("prepared_transfer_url", 3255 prepared_transfer_url)), 3256 GNUNET_JSON_pack_allow_null ( 3257 GNUNET_JSON_pack_string ("bank_label", 3258 bank_label)), 3259 GNUNET_JSON_pack_int64 ("priority", 3260 priority), 3261 GNUNET_JSON_pack_timestamp ("validity_start", 3262 now), 3263 GNUNET_JSON_pack_data_auto ("master_sig_add", 3264 &master_sig_add), 3265 GNUNET_JSON_pack_data_auto ("master_sig_wire", 3266 &master_sig_wire))); 3267 next (args + num_args); 3268 } 3269 3270 3271 /** 3272 * Disable wire account. 3273 * 3274 * @param args the array of command-line arguments to process next; 3275 * args[0] must be the payto URI of the account to disable 3276 */ 3277 static void 3278 do_del_wire (char *const *args) 3279 { 3280 struct TALER_MasterSignatureP master_sig; 3281 struct GNUNET_TIME_Timestamp now; 3282 struct TALER_FullPayto payto_uri = { 3283 .full_payto = args[0] 3284 }; 3285 3286 if (NULL != in) 3287 { 3288 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3289 "Downloaded data was not consumed, not deleting wire account\n") 3290 ; 3291 GNUNET_SCHEDULER_shutdown (); 3292 global_ret = EXIT_FAILURE; 3293 return; 3294 } 3295 if (NULL == args[0]) 3296 { 3297 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3298 "You must specify a payto://-URI with this subcommand\n"); 3299 GNUNET_SCHEDULER_shutdown (); 3300 global_ret = EXIT_INVALIDARGUMENT; 3301 return; 3302 } 3303 if (GNUNET_OK != 3304 load_offline_key (GNUNET_NO)) 3305 return; 3306 now = GNUNET_TIME_timestamp_get (); 3307 TALER_exchange_offline_wire_del_sign (payto_uri, 3308 now, 3309 &master_priv, 3310 &master_sig); 3311 output_operation (OP_DISABLE_WIRE, 3312 GNUNET_JSON_PACK ( 3313 TALER_JSON_pack_full_payto ("payto_uri", 3314 payto_uri), 3315 GNUNET_JSON_pack_timestamp ("validity_end", 3316 now), 3317 GNUNET_JSON_pack_data_auto ("master_sig", 3318 &master_sig))); 3319 next (args + 1); 3320 } 3321 3322 3323 /** 3324 * Set wire fees for the given year. 3325 * 3326 * @param args the array of command-line arguments to process next; 3327 * args[0] must be the year, args[1] the wire method, args[2] the wire fee and args[3] 3328 * the closing fee. 3329 */ 3330 static void 3331 do_set_wire_fee (char *const *args) 3332 { 3333 struct TALER_MasterSignatureP master_sig; 3334 char dummy; 3335 unsigned int year; 3336 struct TALER_WireFeeSet fees; 3337 struct GNUNET_TIME_Timestamp start_time; 3338 struct GNUNET_TIME_Timestamp end_time; 3339 3340 if (NULL != in) 3341 { 3342 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3343 "Downloaded data was not consumed, not setting wire fee\n"); 3344 GNUNET_SCHEDULER_shutdown (); 3345 global_ret = EXIT_FAILURE; 3346 return; 3347 } 3348 if ( (NULL == args[0]) || 3349 (NULL == args[1]) || 3350 (NULL == args[2]) || 3351 (NULL == args[3]) || 3352 ( (1 != sscanf (args[0], 3353 "%u%c", 3354 &year, 3355 &dummy)) && 3356 (0 != strcasecmp ("now", 3357 args[0])) ) || 3358 (GNUNET_OK != 3359 TALER_string_to_amount (args[2], 3360 &fees.wire)) || 3361 (GNUNET_OK != 3362 TALER_string_to_amount (args[3], 3363 &fees.closing)) ) 3364 { 3365 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3366 "You must use YEAR, METHOD, WIRE-FEE, and CLOSING-FEE as arguments for this subcommand\n"); 3367 GNUNET_SCHEDULER_shutdown (); 3368 global_ret = EXIT_INVALIDARGUMENT; 3369 return; 3370 } 3371 if (0 == strcasecmp ("now", 3372 args[0])) 3373 year = GNUNET_TIME_get_current_year (); 3374 if (GNUNET_OK != 3375 load_offline_key (GNUNET_NO)) 3376 return; 3377 start_time = GNUNET_TIME_absolute_to_timestamp ( 3378 GNUNET_TIME_year_to_time (year)); 3379 end_time = GNUNET_TIME_absolute_to_timestamp ( 3380 GNUNET_TIME_year_to_time (year + 1)); 3381 3382 TALER_exchange_offline_wire_fee_sign (args[1], 3383 start_time, 3384 end_time, 3385 &fees, 3386 &master_priv, 3387 &master_sig); 3388 output_operation (OP_SET_WIRE_FEE, 3389 GNUNET_JSON_PACK ( 3390 GNUNET_JSON_pack_string ("wire_method", 3391 args[1]), 3392 GNUNET_JSON_pack_timestamp ("start_time", 3393 start_time), 3394 GNUNET_JSON_pack_timestamp ("end_time", 3395 end_time), 3396 TALER_JSON_pack_amount ("wire_fee", 3397 &fees.wire), 3398 TALER_JSON_pack_amount ("closing_fee", 3399 &fees.closing), 3400 GNUNET_JSON_pack_data_auto ("master_sig", 3401 &master_sig))); 3402 next (args + 4); 3403 } 3404 3405 3406 /** 3407 * Set global fees for the given year. 3408 * 3409 * @param args the array of command-line arguments to process next; 3410 * args[0] must be the year, args[1] the history fee, args[2] 3411 * the account fee and args[3] the purse fee. These are followed by args[4] purse timeout, 3412 * args[5] history expiration. Last is args[6] the (free) purse account limit. 3413 */ 3414 static void 3415 do_set_global_fee (char *const *args) 3416 { 3417 struct TALER_MasterSignatureP master_sig; 3418 char dummy; 3419 unsigned int year; 3420 struct TALER_GlobalFeeSet fees; 3421 struct GNUNET_TIME_Relative purse_timeout; 3422 struct GNUNET_TIME_Relative history_expiration; 3423 unsigned int purse_account_limit; 3424 struct GNUNET_TIME_Timestamp start_time; 3425 struct GNUNET_TIME_Timestamp end_time; 3426 3427 if (NULL != in) 3428 { 3429 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3430 "Downloaded data was not consumed, not setting global fee\n"); 3431 GNUNET_SCHEDULER_shutdown (); 3432 global_ret = EXIT_FAILURE; 3433 return; 3434 } 3435 if ( (NULL == args[0]) || 3436 (NULL == args[1]) || 3437 (NULL == args[2]) || 3438 (NULL == args[3]) || 3439 (NULL == args[4]) || 3440 (NULL == args[5]) || 3441 (NULL == args[6]) ) 3442 { 3443 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3444 "You must use YEAR, HISTORY-FEE, ACCOUNT-FEE, PURSE-FEE, PURSE-TIMEOUT, HISTORY-EXPIRATION and PURSE-ACCOUNT-LIMIT as arguments for this subcommand\n"); 3445 GNUNET_SCHEDULER_shutdown (); 3446 global_ret = EXIT_INVALIDARGUMENT; 3447 return; 3448 } 3449 if ( (1 != sscanf (args[0], 3450 "%u%c", 3451 &year, 3452 &dummy)) && 3453 (0 != strcasecmp ("now", 3454 args[0])) ) 3455 { 3456 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3457 "Invalid YEAR given for 'global-fee' subcommand\n"); 3458 GNUNET_SCHEDULER_shutdown (); 3459 global_ret = EXIT_INVALIDARGUMENT; 3460 return; 3461 } 3462 if ( (GNUNET_OK != 3463 TALER_string_to_amount (args[1], 3464 &fees.history)) || 3465 (GNUNET_OK != 3466 TALER_string_to_amount (args[2], 3467 &fees.account)) || 3468 (GNUNET_OK != 3469 TALER_string_to_amount (args[3], 3470 &fees.purse)) ) 3471 { 3472 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3473 "Invalid amount given for 'global-fee' subcommand\n"); 3474 GNUNET_SCHEDULER_shutdown (); 3475 global_ret = EXIT_INVALIDARGUMENT; 3476 return; 3477 } 3478 if (GNUNET_OK != 3479 GNUNET_STRINGS_fancy_time_to_relative (args[4], 3480 &purse_timeout)) 3481 { 3482 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3483 "Invalid purse timeout `%s' given for 'global-fee' subcommand\n", 3484 args[4]); 3485 GNUNET_SCHEDULER_shutdown (); 3486 global_ret = EXIT_INVALIDARGUMENT; 3487 return; 3488 } 3489 if (GNUNET_OK != 3490 GNUNET_STRINGS_fancy_time_to_relative (args[5], 3491 &history_expiration)) 3492 { 3493 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3494 "Invalid history expiratoin `%s' given for 'global-fee' subcommand\n", 3495 args[5]); 3496 GNUNET_SCHEDULER_shutdown (); 3497 global_ret = EXIT_INVALIDARGUMENT; 3498 return; 3499 } 3500 if (1 != sscanf (args[6], 3501 "%u%c", 3502 &purse_account_limit, 3503 &dummy)) 3504 { 3505 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3506 "Invalid purse account limit given for 'global-fee' subcommand\n"); 3507 GNUNET_SCHEDULER_shutdown (); 3508 global_ret = EXIT_INVALIDARGUMENT; 3509 return; 3510 } 3511 if (0 == strcasecmp ("now", 3512 args[0])) 3513 year = GNUNET_TIME_get_current_year (); 3514 if (GNUNET_OK != 3515 load_offline_key (GNUNET_NO)) 3516 return; 3517 start_time = GNUNET_TIME_absolute_to_timestamp ( 3518 GNUNET_TIME_year_to_time (year)); 3519 end_time = GNUNET_TIME_absolute_to_timestamp ( 3520 GNUNET_TIME_year_to_time (year + 1)); 3521 3522 TALER_exchange_offline_global_fee_sign (start_time, 3523 end_time, 3524 &fees, 3525 purse_timeout, 3526 history_expiration, 3527 (uint32_t) purse_account_limit, 3528 &master_priv, 3529 &master_sig); 3530 output_operation (OP_SET_GLOBAL_FEE, 3531 GNUNET_JSON_PACK ( 3532 GNUNET_JSON_pack_timestamp ("start_time", 3533 start_time), 3534 GNUNET_JSON_pack_timestamp ("end_time", 3535 end_time), 3536 TALER_JSON_pack_amount ("history_fee", 3537 &fees.history), 3538 TALER_JSON_pack_amount ("account_fee", 3539 &fees.account), 3540 TALER_JSON_pack_amount ("purse_fee", 3541 &fees.purse), 3542 GNUNET_JSON_pack_time_rel ("purse_timeout", 3543 purse_timeout), 3544 GNUNET_JSON_pack_time_rel ("history_expiration", 3545 history_expiration), 3546 GNUNET_JSON_pack_uint64 ("purse_account_limit", 3547 (uint32_t) purse_account_limit), 3548 GNUNET_JSON_pack_data_auto ("master_sig", 3549 &master_sig))); 3550 next (args + 7); 3551 } 3552 3553 3554 /** 3555 * Drain profits from exchange's escrow account to 3556 * regular exchange account. 3557 * 3558 * @param args the array of command-line arguments to process next; 3559 * args[0] must be the amount, 3560 * args[1] must be the section of the escrow account to drain 3561 * args[2] must be the payto://-URI of the target account 3562 */ 3563 static void 3564 do_drain (char *const *args) 3565 { 3566 struct TALER_WireTransferIdentifierRawP wtid; 3567 struct GNUNET_TIME_Timestamp date; 3568 struct TALER_Amount amount; 3569 const char *account_section; 3570 struct TALER_FullPayto payto_uri; 3571 struct TALER_MasterSignatureP master_sig; 3572 char *err; 3573 3574 if (NULL != in) 3575 { 3576 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3577 "Downloaded data was not consumed, refusing drain\n"); 3578 GNUNET_SCHEDULER_shutdown (); 3579 global_ret = EXIT_FAILURE; 3580 return; 3581 } 3582 if ( (NULL == args[0]) || 3583 (NULL == args[1]) || 3584 (NULL == args[2]) ) 3585 { 3586 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3587 "Drain requires an amount, section name and target payto://-URI as arguments\n"); 3588 GNUNET_SCHEDULER_shutdown (); 3589 global_ret = EXIT_INVALIDARGUMENT; 3590 return; 3591 } 3592 if (GNUNET_OK != 3593 TALER_string_to_amount (args[0], 3594 &amount)) 3595 { 3596 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3597 "Invalid amount `%s' specified for drain\n", 3598 args[0]); 3599 GNUNET_SCHEDULER_shutdown (); 3600 global_ret = EXIT_INVALIDARGUMENT; 3601 return; 3602 } 3603 account_section = args[1]; 3604 payto_uri.full_payto = args[2]; 3605 err = TALER_payto_validate (payto_uri); 3606 if (NULL != err) 3607 { 3608 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3609 "Invalid payto://-URI `%s' specified for drain: %s\n", 3610 payto_uri.full_payto, 3611 err); 3612 GNUNET_free (err); 3613 GNUNET_SCHEDULER_shutdown (); 3614 global_ret = EXIT_INVALIDARGUMENT; 3615 return; 3616 } 3617 if (GNUNET_OK != 3618 load_offline_key (GNUNET_NO)) 3619 return; 3620 GNUNET_CRYPTO_random_block (&wtid, 3621 sizeof (wtid)); 3622 date = GNUNET_TIME_timestamp_get (); 3623 TALER_exchange_offline_profit_drain_sign (&wtid, 3624 date, 3625 &amount, 3626 account_section, 3627 payto_uri, 3628 &master_priv, 3629 &master_sig); 3630 output_operation (OP_DRAIN_PROFITS, 3631 GNUNET_JSON_PACK ( 3632 GNUNET_JSON_pack_data_auto ("wtid", 3633 &wtid), 3634 GNUNET_JSON_pack_string ("account_section", 3635 account_section), 3636 TALER_JSON_pack_full_payto ("payto_uri", 3637 payto_uri), 3638 TALER_JSON_pack_amount ("amount", 3639 &amount), 3640 GNUNET_JSON_pack_timestamp ("date", 3641 date), 3642 GNUNET_JSON_pack_data_auto ("master_sig", 3643 &master_sig))); 3644 next (args + 3); 3645 } 3646 3647 3648 /** 3649 * Add partner. 3650 * 3651 * @param args the array of command-line arguments to process next; 3652 * args[0] must be the partner's master public key, args[1] the partner's 3653 * API base URL, args[2] the wad fee, args[3] the wad frequency, and 3654 * args[4] the year (including possibly 'now') 3655 */ 3656 static void 3657 do_add_partner (char *const *args) 3658 { 3659 struct TALER_MasterPublicKeyP partner_pub; 3660 struct GNUNET_TIME_Timestamp start_date; 3661 struct GNUNET_TIME_Timestamp end_date; 3662 struct GNUNET_TIME_Relative wad_frequency; 3663 struct TALER_Amount wad_fee; 3664 const char *partner_base_url; 3665 struct TALER_MasterSignatureP master_sig; 3666 char dummy; 3667 unsigned int year; 3668 3669 if (NULL != in) 3670 { 3671 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3672 "Downloaded data was not consumed, not adding partner\n"); 3673 GNUNET_SCHEDULER_shutdown (); 3674 global_ret = EXIT_FAILURE; 3675 return; 3676 } 3677 if ( (NULL == args[0]) || 3678 (GNUNET_OK != 3679 GNUNET_STRINGS_string_to_data (args[0], 3680 strlen (args[0]), 3681 &partner_pub, 3682 sizeof (partner_pub))) ) 3683 { 3684 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3685 "You must specify the partner master public key as first argument for this subcommand\n"); 3686 GNUNET_SCHEDULER_shutdown (); 3687 global_ret = EXIT_INVALIDARGUMENT; 3688 return; 3689 } 3690 if ( (NULL == args[1]) || 3691 (0 != strncmp ("http", 3692 args[1], 3693 strlen ("http"))) ) 3694 { 3695 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3696 "You must specify the partner's base URL as the 2nd argument to this subcommand\n"); 3697 GNUNET_SCHEDULER_shutdown (); 3698 global_ret = EXIT_INVALIDARGUMENT; 3699 return; 3700 } 3701 partner_base_url = args[1]; 3702 if ( (NULL == args[2]) || 3703 (GNUNET_OK != 3704 TALER_string_to_amount (args[2], 3705 &wad_fee)) ) 3706 { 3707 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3708 "Invalid amount `%s' specified for wad fee of partner\n", 3709 args[2]); 3710 GNUNET_SCHEDULER_shutdown (); 3711 global_ret = EXIT_INVALIDARGUMENT; 3712 return; 3713 } 3714 if ( (NULL == args[3]) || 3715 (GNUNET_OK != 3716 GNUNET_STRINGS_fancy_time_to_relative (args[3], 3717 &wad_frequency)) ) 3718 { 3719 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3720 "Invalid wad frequency `%s' specified for add partner\n", 3721 args[3]); 3722 GNUNET_SCHEDULER_shutdown (); 3723 global_ret = EXIT_INVALIDARGUMENT; 3724 return; 3725 } 3726 if ( (NULL == args[4]) || 3727 ( (1 != sscanf (args[4], 3728 "%u%c", 3729 &year, 3730 &dummy)) && 3731 (0 != strcasecmp ("now", 3732 args[4])) ) ) 3733 { 3734 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3735 "Invalid year `%s' specified for add partner\n", 3736 args[4]); 3737 GNUNET_SCHEDULER_shutdown (); 3738 global_ret = EXIT_INVALIDARGUMENT; 3739 return; 3740 } 3741 if (0 == strcasecmp ("now", 3742 args[4])) 3743 year = GNUNET_TIME_get_current_year (); 3744 start_date = GNUNET_TIME_absolute_to_timestamp ( 3745 GNUNET_TIME_year_to_time (year)); 3746 end_date = GNUNET_TIME_absolute_to_timestamp ( 3747 GNUNET_TIME_year_to_time (year + 1)); 3748 3749 if (GNUNET_OK != 3750 load_offline_key (GNUNET_NO)) 3751 return; 3752 TALER_exchange_offline_partner_details_sign (&partner_pub, 3753 start_date, 3754 end_date, 3755 wad_frequency, 3756 &wad_fee, 3757 partner_base_url, 3758 &master_priv, 3759 &master_sig); 3760 output_operation (OP_ADD_PARTNER, 3761 GNUNET_JSON_PACK ( 3762 GNUNET_JSON_pack_string ("partner_base_url", 3763 partner_base_url), 3764 GNUNET_JSON_pack_time_rel ("wad_frequency", 3765 wad_frequency), 3766 GNUNET_JSON_pack_timestamp ("start_date", 3767 start_date), 3768 GNUNET_JSON_pack_timestamp ("end_date", 3769 end_date), 3770 GNUNET_JSON_pack_data_auto ("partner_pub", 3771 &partner_pub), 3772 GNUNET_JSON_pack_data_auto ("master_sig", 3773 &master_sig))); 3774 next (args + 5); 3775 } 3776 3777 3778 /** 3779 * Enable or disable AML staff. 3780 * 3781 * @param is_active true to enable, false to disable 3782 * @param args the array of command-line arguments to process next; args[0] must be the AML staff's public key, args[1] the AML staff's legal name, and if @a is_active then args[2] rw (read write) or ro (read only) 3783 */ 3784 static void 3785 do_set_aml_staff (bool is_active, 3786 char *const *args) 3787 { 3788 struct TALER_AmlOfficerPublicKeyP officer_pub; 3789 const char *officer_name; 3790 bool read_only; 3791 struct TALER_MasterSignatureP master_sig; 3792 struct GNUNET_TIME_Timestamp now 3793 = GNUNET_TIME_timestamp_get (); 3794 3795 if (NULL != in) 3796 { 3797 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3798 "Downloaded data was not consumed, not updating AML staff status\n"); 3799 GNUNET_SCHEDULER_shutdown (); 3800 global_ret = EXIT_FAILURE; 3801 return; 3802 } 3803 if ( (NULL == args[0]) || 3804 (GNUNET_OK != 3805 GNUNET_STRINGS_string_to_data (args[0], 3806 strlen (args[0]), 3807 &officer_pub, 3808 sizeof (officer_pub))) ) 3809 { 3810 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3811 "You must specify the AML officer's public key as first argument for this subcommand\n"); 3812 GNUNET_SCHEDULER_shutdown (); 3813 global_ret = EXIT_INVALIDARGUMENT; 3814 return; 3815 } 3816 if (NULL == args[1]) 3817 { 3818 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3819 "You must specify the officer's legal name as the 2nd argument to this subcommand\n"); 3820 GNUNET_SCHEDULER_shutdown (); 3821 global_ret = EXIT_INVALIDARGUMENT; 3822 return; 3823 } 3824 officer_name = args[1]; 3825 if (is_active) 3826 { 3827 if ( (NULL == args[2]) || 3828 ( (0 != strcmp (args[2], 3829 "ro")) && 3830 (0 != strcmp (args[2], 3831 "rw")) ) ) 3832 { 3833 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3834 "You must specify 'ro' or 'rw' (and not `%s') for the access level\n", 3835 args[2]); 3836 GNUNET_SCHEDULER_shutdown (); 3837 global_ret = EXIT_INVALIDARGUMENT; 3838 return; 3839 } 3840 read_only = (0 == strcmp (args[2], 3841 "ro")); 3842 } 3843 else 3844 { 3845 read_only = true; 3846 } 3847 if (GNUNET_OK != 3848 load_offline_key (GNUNET_NO)) 3849 return; 3850 TALER_exchange_offline_aml_officer_status_sign (&officer_pub, 3851 officer_name, 3852 now, 3853 is_active, 3854 read_only, 3855 &master_priv, 3856 &master_sig); 3857 output_operation (OP_UPDATE_AML_STAFF, 3858 GNUNET_JSON_PACK ( 3859 GNUNET_JSON_pack_string ("officer_name", 3860 officer_name), 3861 GNUNET_JSON_pack_timestamp ("change_date", 3862 now), 3863 GNUNET_JSON_pack_bool ("is_active", 3864 is_active), 3865 GNUNET_JSON_pack_bool ("read_only", 3866 read_only), 3867 GNUNET_JSON_pack_data_auto ("officer_pub", 3868 &officer_pub), 3869 GNUNET_JSON_pack_data_auto ("master_sig", 3870 &master_sig))); 3871 next (args + (is_active ? 3 : 2)); 3872 } 3873 3874 3875 /** 3876 * Disable AML staff. 3877 * 3878 * @param args the array of command-line arguments to process next; 3879 * args[0] must be the AML staff's public key, args[1] the AML staff's legal name 3880 */ 3881 static void 3882 disable_aml_staff (char *const *args) 3883 { 3884 do_set_aml_staff (false, 3885 args); 3886 } 3887 3888 3889 /** 3890 * Enable AML staff. 3891 * 3892 * @param args the array of command-line arguments to process next; 3893 * args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only) 3894 */ 3895 static void 3896 enable_aml_staff (char *const *args) 3897 { 3898 do_set_aml_staff (true, 3899 args); 3900 } 3901 3902 3903 /** 3904 * Function called with information about future keys. Dumps the JSON output 3905 * (on success), either into an internal buffer or to stdout (depending on 3906 * whether there are subsequent commands). 3907 * 3908 * @param args the remaining args 3909 * @param mgr response data 3910 */ 3911 static void 3912 download_cb (char *const *args, 3913 const struct TALER_EXCHANGE_GetManagementKeysResponse *mgr) 3914 { 3915 const struct TALER_EXCHANGE_HttpResponse *hr = &mgr->hr; 3916 3917 mgkh = NULL; 3918 switch (hr->http_status) 3919 { 3920 case MHD_HTTP_OK: 3921 break; 3922 default: 3923 if (0 != hr->http_status) 3924 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3925 "Failed to download keys from `%s': %s (HTTP status: %u/%u)\n", 3926 CFG_exchange_url, 3927 hr->hint, 3928 hr->http_status, 3929 (unsigned int) hr->ec); 3930 else 3931 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3932 "Failed to download keys from `%s' (no HTTP response)\n", 3933 CFG_exchange_url); 3934 GNUNET_SCHEDULER_shutdown (); 3935 global_ret = EXIT_FAILURE; 3936 return; 3937 } 3938 in = GNUNET_JSON_PACK ( 3939 GNUNET_JSON_pack_string ("operation", 3940 OP_INPUT_KEYS), 3941 GNUNET_JSON_pack_object_incref ("arguments", 3942 (json_t *) hr->reply)); 3943 if (NULL == args[0]) 3944 { 3945 json_dumpf (in, 3946 stdout, 3947 JSON_INDENT (2)); 3948 json_decref (in); 3949 in = NULL; 3950 } 3951 next (args); 3952 } 3953 3954 3955 /** 3956 * Download future keys. 3957 * 3958 * @param args the array of command-line arguments to process next 3959 */ 3960 static void 3961 do_download (char *const *args) 3962 { 3963 if ( (NULL == CFG_exchange_url) && 3964 (GNUNET_OK != 3965 GNUNET_CONFIGURATION_get_value_string (kcfg, 3966 "exchange", 3967 "BASE_URL", 3968 &CFG_exchange_url)) ) 3969 { 3970 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 3971 "exchange", 3972 "BASE_URL"); 3973 GNUNET_SCHEDULER_shutdown (); 3974 global_ret = EXIT_NOTCONFIGURED; 3975 return; 3976 } 3977 mgkh = TALER_EXCHANGE_get_management_keys_create (ctx, 3978 CFG_exchange_url); 3979 TALER_EXCHANGE_get_management_keys_start (mgkh, 3980 &download_cb, 3981 (void *) args); 3982 } 3983 3984 3985 /** 3986 * Check that the security module keys are the same as before. If we had no 3987 * keys in store before, remember them (Trust On First Use). 3988 * 3989 * @param secmset security module keys 3990 * @return #GNUNET_OK if keys match with what we have in store 3991 * #GNUNET_NO if we had nothing in store but now do 3992 * #GNUNET_SYSERR if keys changed from what we remember or other error 3993 */ 3994 static enum GNUNET_GenericReturnValue 3995 tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset) 3996 { 3997 char *fn; 3998 struct TALER_SecurityModulePublicKeySetP oldset; 3999 ssize_t ret; 4000 4001 if (GNUNET_OK != 4002 GNUNET_CONFIGURATION_get_value_filename (kcfg, 4003 "exchange-offline", 4004 "SECM_TOFU_FILE", 4005 &fn)) 4006 { 4007 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 4008 "exchange-offline", 4009 "SECM_TOFU_FILE"); 4010 return GNUNET_SYSERR; 4011 } 4012 if (GNUNET_OK == 4013 GNUNET_DISK_file_test (fn)) 4014 { 4015 ret = GNUNET_DISK_fn_read (fn, 4016 &oldset, 4017 sizeof (oldset)); 4018 if (GNUNET_SYSERR != ret) 4019 { 4020 if (ret != sizeof (oldset)) 4021 { 4022 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4023 "File `%s' corrupt\n", 4024 fn); 4025 GNUNET_free (fn); 4026 return GNUNET_SYSERR; 4027 } 4028 /* TOFU check */ 4029 if (0 != memcmp (&oldset, 4030 secmset, 4031 sizeof (*secmset))) 4032 { 4033 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4034 "Fatal: security module keys changed (file `%s')!\n", 4035 fn); 4036 GNUNET_free (fn); 4037 return GNUNET_SYSERR; 4038 } 4039 GNUNET_free (fn); 4040 return GNUNET_OK; 4041 } 4042 } 4043 4044 { 4045 char *key; 4046 4047 /* check against SECMOD-keys pinned in configuration */ 4048 if (GNUNET_OK == 4049 GNUNET_CONFIGURATION_get_value_string (kcfg, 4050 "exchange-offline", 4051 "SECM_ESIGN_PUBKEY", 4052 &key)) 4053 { 4054 struct TALER_SecurityModulePublicKeyP k; 4055 4056 if (GNUNET_OK != 4057 GNUNET_STRINGS_string_to_data (key, 4058 strlen (key), 4059 &k, 4060 sizeof (k))) 4061 { 4062 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4063 "exchange-offline", 4064 "SECM_ESIGN_PUBKEY", 4065 "key malformed"); 4066 GNUNET_free (key); 4067 GNUNET_free (fn); 4068 return GNUNET_SYSERR; 4069 } 4070 GNUNET_free (key); 4071 if (0 != 4072 GNUNET_memcmp (&k, 4073 &secmset->eddsa)) 4074 { 4075 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4076 "ESIGN security module key does not match SECM_ESIGN_PUBKEY in configuration\n") 4077 ; 4078 GNUNET_free (fn); 4079 return GNUNET_SYSERR; 4080 } 4081 } 4082 if (GNUNET_OK == 4083 GNUNET_CONFIGURATION_get_value_string (kcfg, 4084 "exchange-offline", 4085 "SECM_DENOM_PUBKEY", 4086 &key)) 4087 { 4088 struct TALER_SecurityModulePublicKeyP k; 4089 4090 if (GNUNET_OK != 4091 GNUNET_STRINGS_string_to_data (key, 4092 strlen (key), 4093 &k, 4094 sizeof (k))) 4095 { 4096 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4097 "exchange-offline", 4098 "SECM_DENOM_PUBKEY", 4099 "key malformed"); 4100 GNUNET_free (key); 4101 GNUNET_free (fn); 4102 return GNUNET_SYSERR; 4103 } 4104 GNUNET_free (key); 4105 if (0 != 4106 GNUNET_memcmp (&k, 4107 &secmset->rsa)) 4108 { 4109 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4110 "DENOM security module key does not match SECM_DENOM_PUBKEY in configuration\n") 4111 ; 4112 GNUNET_free (fn); 4113 return GNUNET_SYSERR; 4114 } 4115 } 4116 if (GNUNET_OK == 4117 GNUNET_CONFIGURATION_get_value_string (kcfg, 4118 "exchange-offline", 4119 "SECM_DENOM_CS_PUBKEY", 4120 &key)) 4121 { 4122 struct TALER_SecurityModulePublicKeyP k; 4123 4124 if (GNUNET_OK != 4125 GNUNET_STRINGS_string_to_data (key, 4126 strlen (key), 4127 &k, 4128 sizeof (k))) 4129 { 4130 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4131 "exchange-offline", 4132 "SECM_DENOM_CS_PUBKEY", 4133 "key malformed"); 4134 GNUNET_free (key); 4135 GNUNET_free (fn); 4136 return GNUNET_SYSERR; 4137 } 4138 GNUNET_free (key); 4139 if (0 != 4140 GNUNET_memcmp (&k, 4141 &secmset->cs)) 4142 { 4143 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4144 "DENOM security module key does not match SECM_DENOM_CS_PUBKEY in configuration\n"); 4145 GNUNET_free (fn); 4146 return GNUNET_SYSERR; 4147 } 4148 } 4149 } 4150 if (GNUNET_OK != 4151 GNUNET_DISK_directory_create_for_file (fn)) 4152 { 4153 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4154 "Failed create directory to store key material in file `%s'\n", 4155 fn); 4156 GNUNET_free (fn); 4157 return GNUNET_SYSERR; 4158 } 4159 /* persist keys for future runs */ 4160 if (GNUNET_OK != 4161 GNUNET_DISK_fn_write (fn, 4162 secmset, 4163 sizeof (oldset), 4164 GNUNET_DISK_PERM_USER_READ)) 4165 { 4166 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4167 "Failed to store key material in file `%s'\n", 4168 fn); 4169 GNUNET_free (fn); 4170 return GNUNET_SYSERR; 4171 } 4172 GNUNET_free (fn); 4173 return GNUNET_NO; 4174 } 4175 4176 4177 /** 4178 * Output @a signkeys for human consumption. 4179 * 4180 * @param secm_pub security module public key used to sign the denominations 4181 * @param signkeys keys to output 4182 * @return #GNUNET_OK on success 4183 */ 4184 static enum GNUNET_GenericReturnValue 4185 show_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub, 4186 const json_t *signkeys) 4187 { 4188 size_t index; 4189 json_t *value; 4190 4191 json_array_foreach (signkeys, index, value) { 4192 const char *err_name; 4193 unsigned int err_line; 4194 struct TALER_ExchangePublicKeyP exchange_pub; 4195 struct TALER_SecurityModuleSignatureP secm_sig; 4196 struct GNUNET_TIME_Timestamp start_time; 4197 struct GNUNET_TIME_Timestamp sign_end; 4198 struct GNUNET_TIME_Timestamp legal_end; 4199 struct GNUNET_TIME_Relative duration; 4200 struct GNUNET_JSON_Specification spec[] = { 4201 GNUNET_JSON_spec_timestamp ("stamp_start", 4202 &start_time), 4203 GNUNET_JSON_spec_timestamp ("stamp_expire", 4204 &sign_end), 4205 GNUNET_JSON_spec_timestamp ("stamp_end", 4206 &legal_end), 4207 GNUNET_JSON_spec_fixed_auto ("key", 4208 &exchange_pub), 4209 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig", 4210 &secm_sig), 4211 GNUNET_JSON_spec_end () 4212 }; 4213 4214 if (GNUNET_OK != 4215 GNUNET_JSON_parse (value, 4216 spec, 4217 &err_name, 4218 &err_line)) 4219 { 4220 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4221 "Invalid input for signing key to 'show': %s#%u at %u (skipping)\n", 4222 err_name, 4223 err_line, 4224 (unsigned int) index); 4225 json_dumpf (value, 4226 stderr, 4227 JSON_INDENT (2)); 4228 global_ret = EXIT_FAILURE; 4229 GNUNET_SCHEDULER_shutdown (); 4230 return GNUNET_SYSERR; 4231 } 4232 duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time, 4233 sign_end.abs_time); 4234 if (GNUNET_OK != 4235 TALER_exchange_secmod_eddsa_verify (&exchange_pub, 4236 start_time, 4237 duration, 4238 secm_pub, 4239 &secm_sig)) 4240 { 4241 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4242 "Invalid security module signature for signing key %s (aborting)\n", 4243 TALER_B2S (&exchange_pub)); 4244 global_ret = EXIT_FAILURE; 4245 GNUNET_SCHEDULER_shutdown (); 4246 return GNUNET_SYSERR; 4247 } 4248 { 4249 char *legal_end_s; 4250 4251 legal_end_s = GNUNET_strdup ( 4252 GNUNET_TIME_timestamp2s (legal_end)); 4253 printf ("EXCHANGE-KEY %s starting at %s (used for: %s, legal end: %s)\n", 4254 TALER_B2S (&exchange_pub), 4255 GNUNET_TIME_timestamp2s (start_time), 4256 GNUNET_TIME_relative2s (duration, 4257 false), 4258 legal_end_s); 4259 GNUNET_free (legal_end_s); 4260 } 4261 } 4262 return GNUNET_OK; 4263 } 4264 4265 4266 /** 4267 * Looks up the AGE_RESTRICTED setting for a denomination in the config and 4268 * returns the age restriction (mask) accordingly. 4269 * 4270 * @param section_name Section in the configuration for the particular 4271 * denomination. 4272 */ 4273 static struct TALER_AgeMask 4274 load_age_mask (const char *section_name) 4275 { 4276 static const struct TALER_AgeMask null_mask = {0}; 4277 enum GNUNET_GenericReturnValue ret; 4278 4279 if (GNUNET_YES != 4280 GNUNET_CONFIGURATION_get_value_yesno (kcfg, 4281 "exchange", 4282 "AGE_RESTRICTION_ENABLED")) 4283 return null_mask; 4284 4285 if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value ( 4286 kcfg, 4287 section_name, 4288 "AGE_RESTRICTED"))) 4289 return null_mask; 4290 4291 ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg, 4292 section_name, 4293 "AGE_RESTRICTED"); 4294 if (GNUNET_SYSERR == ret) 4295 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4296 section_name, 4297 "AGE_RESTRICTED", 4298 "Value must be YES or NO\n"); 4299 if (GNUNET_YES == ret) 4300 { 4301 char *groups = NULL; 4302 struct TALER_AgeMask mask = {0}; 4303 4304 if (GNUNET_OK != 4305 GNUNET_CONFIGURATION_get_value_string (kcfg, 4306 "exchange", 4307 "AGE_GROUPS", 4308 &groups)) 4309 groups = GNUNET_strdup ("8:10:12:14:16:18:21"); 4310 if (GNUNET_OK == 4311 TALER_parse_age_group_string (groups, &mask)) 4312 { 4313 GNUNET_free (groups); 4314 return mask; 4315 } 4316 GNUNET_free (groups); 4317 } 4318 4319 return null_mask; 4320 } 4321 4322 4323 /** 4324 * Output @a denomkeys for human consumption. 4325 * 4326 * @param secm_pub_rsa security module public key used to sign the RSA denominations 4327 * @param secm_pub_cs security module public key used to sign the CS denominations 4328 * @param denomkeys keys to output 4329 * @return #GNUNET_OK on success 4330 */ 4331 static enum GNUNET_GenericReturnValue 4332 show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, 4333 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs, 4334 const json_t *denomkeys) 4335 { 4336 size_t index; 4337 json_t *value; 4338 4339 json_array_foreach (denomkeys, index, value) { 4340 const char *err_name; 4341 unsigned int err_line; 4342 const char *section_name; 4343 struct TALER_DenominationPublicKey denom_pub; 4344 struct GNUNET_TIME_Timestamp stamp_start; 4345 struct GNUNET_TIME_Timestamp stamp_expire_withdraw; 4346 struct GNUNET_TIME_Timestamp stamp_expire_deposit; 4347 struct GNUNET_TIME_Timestamp stamp_expire_legal; 4348 struct TALER_Amount coin_value; 4349 struct TALER_DenomFeeSet fees; 4350 struct TALER_SecurityModuleSignatureP secm_sig; 4351 struct GNUNET_JSON_Specification spec[] = { 4352 GNUNET_JSON_spec_string ("section_name", 4353 §ion_name), 4354 TALER_JSON_spec_denom_pub ("denom_pub", 4355 &denom_pub), 4356 TALER_JSON_spec_amount ("value", 4357 currency, 4358 &coin_value), 4359 TALER_JSON_SPEC_DENOM_FEES ("fee", 4360 currency, 4361 &fees), 4362 GNUNET_JSON_spec_timestamp ("stamp_start", 4363 &stamp_start), 4364 GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw", 4365 &stamp_expire_withdraw), 4366 GNUNET_JSON_spec_timestamp ("stamp_expire_deposit", 4367 &stamp_expire_deposit), 4368 GNUNET_JSON_spec_timestamp ("stamp_expire_legal", 4369 &stamp_expire_legal), 4370 GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig", 4371 &secm_sig), 4372 GNUNET_JSON_spec_end () 4373 }; 4374 struct GNUNET_TIME_Relative duration; 4375 struct TALER_DenominationHashP h_denom_pub; 4376 enum GNUNET_GenericReturnValue ok; 4377 4378 if (GNUNET_OK != 4379 GNUNET_JSON_parse (value, 4380 spec, 4381 &err_name, 4382 &err_line)) 4383 { 4384 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4385 "Invalid input for denomination key to 'show': %s#%u at %u (skipping)\n", 4386 err_name, 4387 err_line, 4388 (unsigned int) index); 4389 json_dumpf (value, 4390 stderr, 4391 JSON_INDENT (2)); 4392 GNUNET_JSON_parse_free (spec); 4393 global_ret = EXIT_FAILURE; 4394 GNUNET_SCHEDULER_shutdown (); 4395 return GNUNET_SYSERR; 4396 } 4397 duration = GNUNET_TIME_absolute_get_difference ( 4398 stamp_start.abs_time, 4399 stamp_expire_withdraw.abs_time); 4400 denom_pub.age_mask = load_age_mask (section_name); 4401 TALER_denom_pub_hash (&denom_pub, 4402 &h_denom_pub); 4403 switch (denom_pub.bsign_pub_key->cipher) 4404 { 4405 case GNUNET_CRYPTO_BSA_RSA: 4406 { 4407 struct TALER_RsaPubHashP h_rsa; 4408 4409 TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key, 4410 &h_rsa); 4411 ok = TALER_exchange_secmod_rsa_verify (&h_rsa, 4412 section_name, 4413 stamp_start, 4414 duration, 4415 secm_pub_rsa, 4416 &secm_sig); 4417 } 4418 break; 4419 case GNUNET_CRYPTO_BSA_CS: 4420 { 4421 struct TALER_CsPubHashP h_cs; 4422 4423 TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key, 4424 &h_cs); 4425 ok = TALER_exchange_secmod_cs_verify (&h_cs, 4426 section_name, 4427 stamp_start, 4428 duration, 4429 secm_pub_cs, 4430 &secm_sig); 4431 } 4432 break; 4433 default: 4434 GNUNET_break (0); 4435 ok = GNUNET_SYSERR; 4436 break; 4437 } 4438 if (GNUNET_OK != ok) 4439 { 4440 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4441 "Invalid security module signature for denomination key %s (aborting)\n", 4442 GNUNET_h2s (&h_denom_pub.hash)); 4443 global_ret = EXIT_FAILURE; 4444 GNUNET_JSON_parse_free (spec); 4445 GNUNET_SCHEDULER_shutdown (); 4446 return GNUNET_SYSERR; 4447 } 4448 4449 { 4450 char *withdraw_fee_s; 4451 char *deposit_fee_s; 4452 char *refresh_fee_s; 4453 char *refund_fee_s; 4454 char *deposit_s; 4455 char *legal_s; 4456 4457 withdraw_fee_s = TALER_amount_to_string (&fees.withdraw); 4458 deposit_fee_s = TALER_amount_to_string (&fees.deposit); 4459 refresh_fee_s = TALER_amount_to_string (&fees.refresh); 4460 refund_fee_s = TALER_amount_to_string (&fees.refund); 4461 deposit_s = GNUNET_strdup ( 4462 GNUNET_TIME_timestamp2s (stamp_expire_deposit)); 4463 legal_s = GNUNET_strdup ( 4464 GNUNET_TIME_timestamp2s (stamp_expire_legal)); 4465 4466 printf ( 4467 "DENOMINATION-KEY(%s) %s of value %s starting at %s " 4468 "(used for: %s, deposit until: %s legal end: %s) with fees %s/%s/%s/%s\n", 4469 section_name, 4470 TALER_B2S (&h_denom_pub), 4471 TALER_amount2s (&coin_value), 4472 GNUNET_TIME_timestamp2s (stamp_start), 4473 GNUNET_TIME_relative2s (duration, 4474 false), 4475 deposit_s, 4476 legal_s, 4477 withdraw_fee_s, 4478 deposit_fee_s, 4479 refresh_fee_s, 4480 refund_fee_s); 4481 GNUNET_free (withdraw_fee_s); 4482 GNUNET_free (deposit_fee_s); 4483 GNUNET_free (refresh_fee_s); 4484 GNUNET_free (refund_fee_s); 4485 GNUNET_free (deposit_s); 4486 GNUNET_free (legal_s); 4487 } 4488 4489 GNUNET_JSON_parse_free (spec); 4490 } 4491 return GNUNET_OK; 4492 } 4493 4494 4495 /** 4496 * Parse the input of exchange keys for the 'show' and 'sign' commands. 4497 * 4498 * @param command_name name of the command, for logging 4499 * @return NULL on error, otherwise the keys details to be free'd by caller 4500 */ 4501 static json_t * 4502 parse_keys_input (const char *command_name) 4503 { 4504 const char *op_str; 4505 json_t *keys; 4506 struct GNUNET_JSON_Specification spec[] = { 4507 GNUNET_JSON_spec_json ("arguments", 4508 &keys), 4509 GNUNET_JSON_spec_string ("operation", 4510 &op_str), 4511 GNUNET_JSON_spec_end () 4512 }; 4513 const char *err_name; 4514 unsigned int err_line; 4515 4516 if (NULL == in) 4517 { 4518 json_error_t err; 4519 4520 in = json_loadf (stdin, 4521 JSON_REJECT_DUPLICATES, 4522 &err); 4523 if (NULL == in) 4524 { 4525 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4526 "Failed to read JSON input: %s at %d:%s (offset: %d)\n", 4527 err.text, 4528 err.line, 4529 err.source, 4530 err.position); 4531 global_ret = EXIT_FAILURE; 4532 GNUNET_SCHEDULER_shutdown (); 4533 return NULL; 4534 } 4535 } 4536 if (GNUNET_OK != 4537 GNUNET_JSON_parse (in, 4538 spec, 4539 &err_name, 4540 &err_line)) 4541 { 4542 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4543 "Invalid input to '%s': %s#%u (skipping)\n", 4544 command_name, 4545 err_name, 4546 err_line); 4547 json_dumpf (in, 4548 stderr, 4549 JSON_INDENT (2)); 4550 global_ret = EXIT_FAILURE; 4551 GNUNET_SCHEDULER_shutdown (); 4552 return NULL; 4553 } 4554 if (0 != strcmp (op_str, 4555 OP_INPUT_KEYS)) 4556 { 4557 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4558 "Invalid input to '%s' : operation is `%s', expected `%s'\n", 4559 command_name, 4560 op_str, 4561 OP_INPUT_KEYS); 4562 GNUNET_JSON_parse_free (spec); 4563 global_ret = EXIT_FAILURE; 4564 GNUNET_SCHEDULER_shutdown (); 4565 return NULL; 4566 } 4567 json_decref (in); 4568 in = NULL; 4569 return keys; 4570 } 4571 4572 4573 /** 4574 * Show future keys. 4575 * 4576 * @param args the array of command-line arguments to process next 4577 */ 4578 static void 4579 do_show (char *const *args) 4580 { 4581 json_t *keys; 4582 const char *err_name; 4583 unsigned int err_line; 4584 const json_t *denomkeys; 4585 const json_t *signkeys; 4586 struct TALER_MasterPublicKeyP mpub; 4587 struct TALER_SecurityModulePublicKeySetP secmset; 4588 struct GNUNET_JSON_Specification spec[] = { 4589 GNUNET_JSON_spec_array_const ("future_denoms", 4590 &denomkeys), 4591 GNUNET_JSON_spec_array_const ("future_signkeys", 4592 &signkeys), 4593 GNUNET_JSON_spec_fixed_auto ("master_pub", 4594 &mpub), 4595 GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key", 4596 &secmset.rsa), 4597 GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key", 4598 &secmset.cs), 4599 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key", 4600 &secmset.eddsa), 4601 GNUNET_JSON_spec_end () 4602 }; 4603 4604 keys = parse_keys_input ("show"); 4605 if (NULL == keys) 4606 return; 4607 4608 if (GNUNET_OK != 4609 load_offline_key (GNUNET_NO)) 4610 { 4611 json_decref (keys); 4612 return; 4613 } 4614 if (GNUNET_OK != 4615 GNUNET_JSON_parse (keys, 4616 spec, 4617 &err_name, 4618 &err_line)) 4619 { 4620 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4621 "Invalid input to 'show': %s #%u (skipping)\n", 4622 err_name, 4623 err_line); 4624 json_dumpf (keys, 4625 stderr, 4626 JSON_INDENT (2)); 4627 global_ret = EXIT_FAILURE; 4628 GNUNET_SCHEDULER_shutdown (); 4629 json_decref (keys); 4630 return; 4631 } 4632 if (0 != 4633 GNUNET_memcmp (&master_pub, 4634 &mpub)) 4635 { 4636 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4637 "Fatal: exchange uses different master key!\n"); 4638 global_ret = EXIT_FAILURE; 4639 GNUNET_SCHEDULER_shutdown (); 4640 json_decref (keys); 4641 return; 4642 } 4643 if (GNUNET_SYSERR == 4644 tofu_check (&secmset)) 4645 { 4646 global_ret = EXIT_FAILURE; 4647 GNUNET_SCHEDULER_shutdown (); 4648 json_decref (keys); 4649 return; 4650 } 4651 if ( (GNUNET_OK != 4652 show_signkeys (&secmset.eddsa, 4653 signkeys)) || 4654 (GNUNET_OK != 4655 show_denomkeys (&secmset.rsa, 4656 &secmset.cs, 4657 denomkeys)) ) 4658 { 4659 global_ret = EXIT_FAILURE; 4660 GNUNET_SCHEDULER_shutdown (); 4661 json_decref (keys); 4662 return; 4663 } 4664 json_decref (keys); 4665 next (args); 4666 } 4667 4668 4669 /** 4670 * Sign @a signkeys with offline key. 4671 * 4672 * @param secm_pub security module public key used to sign the denominations 4673 * @param signkeys keys to output 4674 * @param[in,out] result array where to output the signatures 4675 * @return #GNUNET_OK on success 4676 */ 4677 static enum GNUNET_GenericReturnValue 4678 sign_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub, 4679 const json_t *signkeys, 4680 json_t *result) 4681 { 4682 size_t index; 4683 json_t *value; 4684 4685 json_array_foreach (signkeys, index, value) { 4686 const char *err_name; 4687 unsigned int err_line; 4688 struct TALER_ExchangePublicKeyP exchange_pub; 4689 struct TALER_SecurityModuleSignatureP secm_sig; 4690 struct GNUNET_TIME_Timestamp start_time; 4691 struct GNUNET_TIME_Timestamp sign_end; 4692 struct GNUNET_TIME_Timestamp legal_end; 4693 struct GNUNET_TIME_Relative duration; 4694 struct GNUNET_JSON_Specification spec[] = { 4695 GNUNET_JSON_spec_timestamp ("stamp_start", 4696 &start_time), 4697 GNUNET_JSON_spec_timestamp ("stamp_expire", 4698 &sign_end), 4699 GNUNET_JSON_spec_timestamp ("stamp_end", 4700 &legal_end), 4701 GNUNET_JSON_spec_fixed_auto ("key", 4702 &exchange_pub), 4703 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig", 4704 &secm_sig), 4705 GNUNET_JSON_spec_end () 4706 }; 4707 4708 if (GNUNET_OK != 4709 GNUNET_JSON_parse (value, 4710 spec, 4711 &err_name, 4712 &err_line)) 4713 { 4714 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4715 "Invalid input for signing key to 'show': %s #%u at %u (skipping)\n", 4716 err_name, 4717 err_line, 4718 (unsigned int) index); 4719 json_dumpf (value, 4720 stderr, 4721 JSON_INDENT (2)); 4722 global_ret = EXIT_FAILURE; 4723 GNUNET_SCHEDULER_shutdown (); 4724 return GNUNET_SYSERR; 4725 } 4726 4727 duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time, 4728 sign_end.abs_time); 4729 if (GNUNET_OK != 4730 TALER_exchange_secmod_eddsa_verify (&exchange_pub, 4731 start_time, 4732 duration, 4733 secm_pub, 4734 &secm_sig)) 4735 { 4736 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4737 "Invalid security module signature for signing key %s (aborting)\n", 4738 TALER_B2S (&exchange_pub)); 4739 global_ret = EXIT_FAILURE; 4740 GNUNET_SCHEDULER_shutdown (); 4741 GNUNET_JSON_parse_free (spec); 4742 return GNUNET_SYSERR; 4743 } 4744 { 4745 struct TALER_MasterSignatureP master_sig; 4746 4747 TALER_exchange_offline_signkey_validity_sign (&exchange_pub, 4748 start_time, 4749 sign_end, 4750 legal_end, 4751 &master_priv, 4752 &master_sig); 4753 GNUNET_assert (0 == 4754 json_array_append_new ( 4755 result, 4756 GNUNET_JSON_PACK ( 4757 GNUNET_JSON_pack_data_auto ("exchange_pub", 4758 &exchange_pub), 4759 GNUNET_JSON_pack_data_auto ("master_sig", 4760 &master_sig)))); 4761 } 4762 GNUNET_JSON_parse_free (spec); 4763 } 4764 return GNUNET_OK; 4765 } 4766 4767 4768 /** 4769 * Sign @a denomkeys with offline key. 4770 * 4771 * @param secm_pub_rsa security module public key used to sign the RSA denominations 4772 * @param secm_pub_cs security module public key used to sign the CS denominations 4773 * @param denomkeys keys to output 4774 * @param[in,out] result array where to output the signatures 4775 * @return #GNUNET_OK on success 4776 */ 4777 static enum GNUNET_GenericReturnValue 4778 sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, 4779 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs, 4780 const json_t *denomkeys, 4781 json_t *result) 4782 { 4783 size_t index; 4784 json_t *value; 4785 4786 json_array_foreach (denomkeys, index, value) { 4787 const char *err_name; 4788 unsigned int err_line; 4789 const char *section_name; 4790 struct TALER_DenominationPublicKey denom_pub; 4791 struct GNUNET_TIME_Timestamp stamp_start; 4792 struct GNUNET_TIME_Timestamp stamp_expire_withdraw; 4793 struct GNUNET_TIME_Timestamp stamp_expire_deposit; 4794 struct GNUNET_TIME_Timestamp stamp_expire_legal; 4795 struct TALER_Amount coin_value; 4796 struct TALER_DenomFeeSet fees; 4797 struct TALER_SecurityModuleSignatureP secm_sig; 4798 struct GNUNET_JSON_Specification spec[] = { 4799 GNUNET_JSON_spec_string ("section_name", 4800 §ion_name), 4801 TALER_JSON_spec_denom_pub ("denom_pub", 4802 &denom_pub), 4803 TALER_JSON_spec_amount ("value", 4804 currency, 4805 &coin_value), 4806 TALER_JSON_SPEC_DENOM_FEES ("fee", 4807 currency, 4808 &fees), 4809 GNUNET_JSON_spec_timestamp ("stamp_start", 4810 &stamp_start), 4811 GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw", 4812 &stamp_expire_withdraw), 4813 GNUNET_JSON_spec_timestamp ("stamp_expire_deposit", 4814 &stamp_expire_deposit), 4815 GNUNET_JSON_spec_timestamp ("stamp_expire_legal", 4816 &stamp_expire_legal), 4817 GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig", 4818 &secm_sig), 4819 GNUNET_JSON_spec_end () 4820 }; 4821 struct GNUNET_TIME_Relative duration; 4822 struct TALER_DenominationHashP h_denom_pub; 4823 4824 if (GNUNET_OK != 4825 GNUNET_JSON_parse (value, 4826 spec, 4827 &err_name, 4828 &err_line)) 4829 { 4830 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4831 "Invalid input for denomination key to 'sign': %s #%u at %u (skipping)\n", 4832 err_name, 4833 err_line, 4834 (unsigned int) index); 4835 json_dumpf (value, 4836 stderr, 4837 JSON_INDENT (2)); 4838 GNUNET_JSON_parse_free (spec); 4839 global_ret = EXIT_FAILURE; 4840 GNUNET_SCHEDULER_shutdown (); 4841 return GNUNET_SYSERR; 4842 } 4843 duration = GNUNET_TIME_absolute_get_difference ( 4844 stamp_start.abs_time, 4845 stamp_expire_withdraw.abs_time); 4846 4847 /* Load the age mask, if applicable to this denomination */ 4848 denom_pub.age_mask = load_age_mask (section_name); 4849 4850 TALER_denom_pub_hash (&denom_pub, 4851 &h_denom_pub); 4852 4853 switch (denom_pub.bsign_pub_key->cipher) 4854 { 4855 case GNUNET_CRYPTO_BSA_RSA: 4856 { 4857 struct TALER_RsaPubHashP h_rsa; 4858 4859 TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key, 4860 &h_rsa); 4861 if (GNUNET_OK != 4862 TALER_exchange_secmod_rsa_verify (&h_rsa, 4863 section_name, 4864 stamp_start, 4865 duration, 4866 secm_pub_rsa, 4867 &secm_sig)) 4868 { 4869 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4870 "Invalid security module signature for denomination key %s (aborting)\n", 4871 GNUNET_h2s (&h_denom_pub.hash)); 4872 global_ret = EXIT_FAILURE; 4873 GNUNET_SCHEDULER_shutdown (); 4874 GNUNET_JSON_parse_free (spec); 4875 return GNUNET_SYSERR; 4876 } 4877 } 4878 break; 4879 case GNUNET_CRYPTO_BSA_CS: 4880 { 4881 struct TALER_CsPubHashP h_cs; 4882 4883 TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key, 4884 &h_cs); 4885 if (GNUNET_OK != 4886 TALER_exchange_secmod_cs_verify (&h_cs, 4887 section_name, 4888 stamp_start, 4889 duration, 4890 secm_pub_cs, 4891 &secm_sig)) 4892 { 4893 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4894 "Invalid security module signature for denomination key %s (aborting)\n", 4895 GNUNET_h2s (&h_denom_pub.hash)); 4896 global_ret = EXIT_FAILURE; 4897 GNUNET_SCHEDULER_shutdown (); 4898 GNUNET_JSON_parse_free (spec); 4899 return GNUNET_SYSERR; 4900 } 4901 } 4902 break; 4903 default: 4904 global_ret = EXIT_FAILURE; 4905 GNUNET_SCHEDULER_shutdown (); 4906 GNUNET_JSON_parse_free (spec); 4907 return GNUNET_SYSERR; 4908 } 4909 4910 { 4911 struct TALER_MasterSignatureP master_sig; 4912 4913 TALER_exchange_offline_denom_validity_sign (&h_denom_pub, 4914 stamp_start, 4915 stamp_expire_withdraw, 4916 stamp_expire_deposit, 4917 stamp_expire_legal, 4918 &coin_value, 4919 &fees, 4920 &master_priv, 4921 &master_sig); 4922 GNUNET_assert (0 == 4923 json_array_append_new ( 4924 result, 4925 GNUNET_JSON_PACK ( 4926 GNUNET_JSON_pack_data_auto ("h_denom_pub", 4927 &h_denom_pub), 4928 GNUNET_JSON_pack_data_auto ("master_sig", 4929 &master_sig)))); 4930 } 4931 GNUNET_JSON_parse_free (spec); 4932 } 4933 return GNUNET_OK; 4934 } 4935 4936 4937 /** 4938 * Sign future keys. 4939 * 4940 * @param args the array of command-line arguments to process next 4941 */ 4942 static void 4943 do_sign (char *const *args) 4944 { 4945 json_t *keys; 4946 const char *err_name; 4947 unsigned int err_line; 4948 const json_t *denomkeys; 4949 const json_t *signkeys; 4950 struct TALER_MasterPublicKeyP mpub; 4951 struct TALER_SecurityModulePublicKeySetP secmset; 4952 struct GNUNET_JSON_Specification spec[] = { 4953 GNUNET_JSON_spec_array_const ("future_denoms", 4954 &denomkeys), 4955 GNUNET_JSON_spec_array_const ("future_signkeys", 4956 &signkeys), 4957 GNUNET_JSON_spec_fixed_auto ("master_pub", 4958 &mpub), 4959 GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key", 4960 &secmset.rsa), 4961 GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key", 4962 &secmset.cs), 4963 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key", 4964 &secmset.eddsa), 4965 GNUNET_JSON_spec_end () 4966 }; 4967 4968 keys = parse_keys_input ("sign"); 4969 if (NULL == keys) 4970 return; 4971 if (GNUNET_OK != 4972 load_offline_key (GNUNET_NO)) 4973 { 4974 json_decref (keys); 4975 return; 4976 } 4977 if (GNUNET_OK != 4978 GNUNET_JSON_parse (keys, 4979 spec, 4980 &err_name, 4981 &err_line)) 4982 { 4983 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4984 "Invalid input to 'sign' : %s #%u (skipping)\n", 4985 err_name, 4986 err_line); 4987 json_dumpf (keys, 4988 stderr, 4989 JSON_INDENT (2)); 4990 global_ret = EXIT_FAILURE; 4991 GNUNET_SCHEDULER_shutdown (); 4992 json_decref (keys); 4993 return; 4994 } 4995 if (0 != 4996 GNUNET_memcmp (&master_pub, 4997 &mpub)) 4998 { 4999 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5000 "Fatal: exchange uses different master key!\n"); 5001 global_ret = EXIT_FAILURE; 5002 GNUNET_SCHEDULER_shutdown (); 5003 json_decref (keys); 5004 return; 5005 } 5006 if (GNUNET_SYSERR == 5007 tofu_check (&secmset)) 5008 { 5009 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5010 "Fatal: security module keys changed!\n"); 5011 global_ret = EXIT_FAILURE; 5012 GNUNET_SCHEDULER_shutdown (); 5013 json_decref (keys); 5014 return; 5015 } 5016 { 5017 json_t *signkey_sig_array = json_array (); 5018 json_t *denomkey_sig_array = json_array (); 5019 5020 GNUNET_assert (NULL != signkey_sig_array); 5021 GNUNET_assert (NULL != denomkey_sig_array); 5022 if ( (GNUNET_OK != 5023 sign_signkeys (&secmset.eddsa, 5024 signkeys, 5025 signkey_sig_array)) || 5026 (GNUNET_OK != 5027 sign_denomkeys (&secmset.rsa, 5028 &secmset.cs, 5029 denomkeys, 5030 denomkey_sig_array)) ) 5031 { 5032 global_ret = EXIT_FAILURE; 5033 GNUNET_SCHEDULER_shutdown (); 5034 json_decref (signkey_sig_array); 5035 json_decref (denomkey_sig_array); 5036 json_decref (keys); 5037 return; 5038 } 5039 5040 output_operation (OP_UPLOAD_SIGS, 5041 GNUNET_JSON_PACK ( 5042 GNUNET_JSON_pack_array_steal ("denom_sigs", 5043 denomkey_sig_array), 5044 GNUNET_JSON_pack_array_steal ("signkey_sigs", 5045 signkey_sig_array))); 5046 } 5047 json_decref (keys); 5048 next (args); 5049 } 5050 5051 5052 /** 5053 * Setup and output offline signing key. 5054 * 5055 * @param args the array of command-line arguments to process next 5056 */ 5057 static void 5058 do_setup (char *const *args) 5059 { 5060 if (GNUNET_OK != 5061 load_offline_key (GNUNET_YES)) 5062 { 5063 global_ret = EXIT_NOPERMISSION; 5064 return; 5065 } 5066 if (NULL != *args) 5067 { 5068 output_operation (OP_SETUP, 5069 GNUNET_JSON_PACK ( 5070 GNUNET_JSON_pack_data_auto ("exchange_offline_pub", 5071 &master_pub))); 5072 } 5073 5074 else 5075 { 5076 char *pub_s; 5077 5078 pub_s = GNUNET_STRINGS_data_to_string_alloc (&master_pub, 5079 sizeof (master_pub)); 5080 fprintf (stdout, 5081 "%s\n", 5082 pub_s); 5083 GNUNET_free (pub_s); 5084 } 5085 if ( (NULL != *args) && 5086 (0 == strcmp (*args, 5087 "-")) ) 5088 args++; 5089 next (args); 5090 } 5091 5092 5093 /** 5094 * Dispatch @a args in the @a cmds array. 5095 * 5096 * @param args arguments with subcommand to dispatch 5097 * @param cmds array of possible subcommands to call 5098 */ 5099 static void 5100 cmd_handler (char *const *args, 5101 const struct SubCommand *cmds) 5102 { 5103 nxt = NULL; 5104 for (unsigned int i = 0; NULL != cmds[i].name; i++) 5105 { 5106 if (0 == strcasecmp (cmds[i].name, 5107 args[0])) 5108 { 5109 cmds[i].cb (&args[1]); 5110 return; 5111 } 5112 } 5113 5114 if (0 != strcasecmp ("help", 5115 args[0])) 5116 { 5117 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5118 "Unexpected command `%s'\n", 5119 args[0]); 5120 global_ret = EXIT_INVALIDARGUMENT; 5121 } 5122 5123 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5124 "Supported subcommands:\n"); 5125 for (unsigned int i = 0; NULL != cmds[i].name; i++) 5126 { 5127 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5128 "- %s: %s\n", 5129 cmds[i].name, 5130 cmds[i].help); 5131 } 5132 json_decref (out); 5133 out = NULL; 5134 GNUNET_SCHEDULER_shutdown (); 5135 } 5136 5137 5138 static void 5139 work (void *cls) 5140 { 5141 char *const *args = cls; 5142 struct SubCommand cmds[] = { 5143 { 5144 .name = "setup", 5145 .help = 5146 "initialize offline key signing material and display public offline key", 5147 .cb = &do_setup 5148 }, 5149 { 5150 .name = "download", 5151 .help = 5152 "obtain future public keys from exchange (to be performed online!)", 5153 .cb = &do_download 5154 }, 5155 { 5156 .name = "show", 5157 .help = 5158 "display future public keys from exchange for human review (pass '-' as argument to disable consuming input)", 5159 .cb = &do_show 5160 }, 5161 { 5162 .name = "sign", 5163 .help = "sign all future public keys from the input", 5164 .cb = &do_sign 5165 }, 5166 { 5167 .name = "revoke-denomination", 5168 .help = 5169 "revoke denomination key (hash of public key must be given as argument)", 5170 .cb = &do_revoke_denomination_key 5171 }, 5172 { 5173 .name = "revoke-signkey", 5174 .help = 5175 "revoke exchange online signing key (public key must be given as argument)", 5176 .cb = &do_revoke_signkey 5177 }, 5178 { 5179 .name = "enable-auditor", 5180 .help = 5181 "enable auditor for the exchange (auditor-public key, auditor-URI and auditor-name must be given as arguments)", 5182 .cb = &do_add_auditor 5183 }, 5184 { 5185 .name = "disable-auditor", 5186 .help = 5187 "disable auditor at the exchange (auditor-public key must be given as argument)", 5188 .cb = &do_del_auditor 5189 }, 5190 { 5191 .name = "enable-account", 5192 .help = 5193 "enable wire account of the exchange (payto-URI must be given as argument; for optional arguments see man page)", 5194 .cb = &do_add_wire 5195 }, 5196 { 5197 .name = "disable-account", 5198 .help = 5199 "disable wire account of the exchange (payto-URI must be given as argument)", 5200 .cb = &do_del_wire 5201 }, 5202 { 5203 .name = "wire-fee", 5204 .help = 5205 "sign wire fees for the given year (year, wire method, wire fee, and closing fee must be given as arguments)", 5206 .cb = &do_set_wire_fee 5207 }, 5208 { 5209 .name = "global-fee", 5210 .help = 5211 "sign global fees for the given year (year, history fee, account fee, purse fee, purse timeout, history expiration and the maximum number of free purses per account must be given as arguments)", 5212 .cb = &do_set_global_fee 5213 }, 5214 { 5215 .name = "drain", 5216 .help = 5217 "drain profits from exchange escrow account to regular exchange operator account (amount, debit account configuration section and credit account payto://-URI must be given as arguments)", 5218 .cb = &do_drain 5219 }, 5220 { 5221 .name = "add-partner", 5222 .help = 5223 "add partner exchange for P2P wad transfers (partner master public key, partner base URL, wad fee, wad frequency and validity year must be given as arguments)", 5224 .cb = &do_add_partner 5225 }, 5226 { 5227 .name = "aml-enable", 5228 .help = 5229 "enable AML staff member (staff member public key, legal name and rw (read write) or ro (read only) must be given as arguments)", 5230 .cb = &enable_aml_staff 5231 }, 5232 { 5233 .name = "aml-disable", 5234 .help = 5235 "disable AML staff member (staff member public key and legal name must be given as arguments)", 5236 .cb = &disable_aml_staff 5237 }, 5238 { 5239 .name = "upload", 5240 .help = 5241 "upload operation result to exchange (to be performed online!)", 5242 .cb = &do_upload 5243 }, 5244 /* list terminator */ 5245 { 5246 .name = NULL, 5247 } 5248 }; 5249 (void) cls; 5250 5251 cmd_handler (args, cmds); 5252 } 5253 5254 5255 /** 5256 * Main function that will be run. 5257 * 5258 * @param cls closure 5259 * @param args remaining command-line arguments 5260 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 5261 * @param cfg configuration 5262 */ 5263 static void 5264 run (void *cls, 5265 char *const *args, 5266 const char *cfgfile, 5267 const struct GNUNET_CONFIGURATION_Handle *cfg) 5268 { 5269 (void) cls; 5270 (void) cfgfile; 5271 kcfg = cfg; 5272 5273 /* age restriction is read per-denomination in load_age_mask() from [exchange] */ 5274 5275 5276 if (GNUNET_OK != 5277 TALER_config_get_currency (kcfg, 5278 "exchange", 5279 ¤cy)) 5280 { 5281 global_ret = EXIT_NOTCONFIGURED; 5282 return; 5283 } 5284 5285 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 5286 &rc); 5287 rc = GNUNET_CURL_gnunet_rc_create (ctx); 5288 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 5289 NULL); 5290 next (args); 5291 } 5292 5293 5294 /** 5295 * The main function of the taler-exchange-offline tool. This tool is used to 5296 * create the signing and denomination keys for the exchange. It uses the 5297 * long-term offline private key and generates signatures with it. It also 5298 * supports online operations with the exchange to download its input data and 5299 * to upload its results. Those online operations should be performed on 5300 * another machine in production! 5301 * 5302 * @param argc number of arguments from the command line 5303 * @param argv command line arguments 5304 * @return 0 ok, 1 on error 5305 */ 5306 int 5307 main (int argc, 5308 char *const *argv) 5309 { 5310 struct GNUNET_GETOPT_CommandLineOption options[] = { 5311 GNUNET_GETOPT_OPTION_END 5312 }; 5313 enum GNUNET_GenericReturnValue ret; 5314 5315 ret = GNUNET_PROGRAM_run ( 5316 TALER_EXCHANGE_project_data (), 5317 argc, argv, 5318 "taler-exchange-offline", 5319 gettext_noop ("Operations for offline signing for a Taler exchange"), 5320 options, 5321 &run, NULL); 5322 if (GNUNET_SYSERR == ret) 5323 return EXIT_INVALIDARGUMENT; 5324 if (GNUNET_NO == ret) 5325 return EXIT_SUCCESS; 5326 return global_ret; 5327 } 5328 5329 5330 /* end of taler-exchange-offline.c */