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