taler-exchange-offline.c (162462B)
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 ; 4089 GNUNET_free (fn); 4090 return GNUNET_SYSERR; 4091 } 4092 } 4093 if (GNUNET_OK == 4094 GNUNET_CONFIGURATION_get_value_string (kcfg, 4095 "exchange-offline", 4096 "SECM_DENOM_PUBKEY", 4097 &key)) 4098 { 4099 struct TALER_SecurityModulePublicKeyP k; 4100 4101 if (GNUNET_OK != 4102 GNUNET_STRINGS_string_to_data (key, 4103 strlen (key), 4104 &k, 4105 sizeof (k))) 4106 { 4107 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4108 "exchange-offline", 4109 "SECM_DENOM_PUBKEY", 4110 "key malformed"); 4111 GNUNET_free (key); 4112 GNUNET_free (fn); 4113 return GNUNET_SYSERR; 4114 } 4115 GNUNET_free (key); 4116 if (0 != 4117 GNUNET_memcmp (&k, 4118 &secmset->rsa)) 4119 { 4120 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4121 "DENOM security module key does not match SECM_DENOM_PUBKEY in configuration\n") 4122 ; 4123 GNUNET_free (fn); 4124 return GNUNET_SYSERR; 4125 } 4126 } 4127 if (GNUNET_OK == 4128 GNUNET_CONFIGURATION_get_value_string (kcfg, 4129 "exchange-offline", 4130 "SECM_DENOM_CS_PUBKEY", 4131 &key)) 4132 { 4133 struct TALER_SecurityModulePublicKeyP k; 4134 4135 if (GNUNET_OK != 4136 GNUNET_STRINGS_string_to_data (key, 4137 strlen (key), 4138 &k, 4139 sizeof (k))) 4140 { 4141 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4142 "exchange-offline", 4143 "SECM_DENOM_CS_PUBKEY", 4144 "key malformed"); 4145 GNUNET_free (key); 4146 GNUNET_free (fn); 4147 return GNUNET_SYSERR; 4148 } 4149 GNUNET_free (key); 4150 if (0 != 4151 GNUNET_memcmp (&k, 4152 &secmset->cs)) 4153 { 4154 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4155 "DENOM security module key does not match SECM_DENOM_CS_PUBKEY in configuration\n"); 4156 GNUNET_free (fn); 4157 return GNUNET_SYSERR; 4158 } 4159 } 4160 } 4161 if (GNUNET_OK != 4162 GNUNET_DISK_directory_create_for_file (fn)) 4163 { 4164 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4165 "Failed create directory to store key material in file `%s'\n", 4166 fn); 4167 GNUNET_free (fn); 4168 return GNUNET_SYSERR; 4169 } 4170 /* persist keys for future runs */ 4171 if (GNUNET_OK != 4172 GNUNET_DISK_fn_write (fn, 4173 secmset, 4174 sizeof (oldset), 4175 GNUNET_DISK_PERM_USER_READ)) 4176 { 4177 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4178 "Failed to store key material in file `%s'\n", 4179 fn); 4180 GNUNET_free (fn); 4181 return GNUNET_SYSERR; 4182 } 4183 GNUNET_free (fn); 4184 return GNUNET_NO; 4185 } 4186 4187 4188 /** 4189 * Output @a signkeys for human consumption. 4190 * 4191 * @param secm_pub security module public key used to sign the denominations 4192 * @param signkeys keys to output 4193 * @return #GNUNET_OK on success 4194 */ 4195 static enum GNUNET_GenericReturnValue 4196 show_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub, 4197 const json_t *signkeys) 4198 { 4199 size_t index; 4200 json_t *value; 4201 4202 json_array_foreach (signkeys, index, value) { 4203 const char *err_name; 4204 unsigned int err_line; 4205 struct TALER_ExchangePublicKeyP exchange_pub; 4206 struct TALER_SecurityModuleSignatureP secm_sig; 4207 struct GNUNET_TIME_Timestamp start_time; 4208 struct GNUNET_TIME_Timestamp sign_end; 4209 struct GNUNET_TIME_Timestamp legal_end; 4210 struct GNUNET_TIME_Relative duration; 4211 struct GNUNET_JSON_Specification spec[] = { 4212 GNUNET_JSON_spec_timestamp ("stamp_start", 4213 &start_time), 4214 GNUNET_JSON_spec_timestamp ("stamp_expire", 4215 &sign_end), 4216 GNUNET_JSON_spec_timestamp ("stamp_end", 4217 &legal_end), 4218 GNUNET_JSON_spec_fixed_auto ("key", 4219 &exchange_pub), 4220 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig", 4221 &secm_sig), 4222 GNUNET_JSON_spec_end () 4223 }; 4224 4225 if (GNUNET_OK != 4226 GNUNET_JSON_parse (value, 4227 spec, 4228 &err_name, 4229 &err_line)) 4230 { 4231 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4232 "Invalid input for signing key to 'show': %s#%u at %u (skipping)\n", 4233 err_name, 4234 err_line, 4235 (unsigned int) index); 4236 json_dumpf (value, 4237 stderr, 4238 JSON_INDENT (2)); 4239 global_ret = EXIT_FAILURE; 4240 GNUNET_SCHEDULER_shutdown (); 4241 return GNUNET_SYSERR; 4242 } 4243 duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time, 4244 sign_end.abs_time); 4245 if (GNUNET_OK != 4246 TALER_exchange_secmod_eddsa_verify (&exchange_pub, 4247 start_time, 4248 duration, 4249 secm_pub, 4250 &secm_sig)) 4251 { 4252 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4253 "Invalid security module signature for signing key %s (aborting)\n", 4254 TALER_B2S (&exchange_pub)); 4255 global_ret = EXIT_FAILURE; 4256 GNUNET_SCHEDULER_shutdown (); 4257 return GNUNET_SYSERR; 4258 } 4259 { 4260 char *legal_end_s; 4261 4262 legal_end_s = GNUNET_strdup ( 4263 GNUNET_TIME_timestamp2s (legal_end)); 4264 printf ("EXCHANGE-KEY %s starting at %s (used for: %s, legal end: %s)\n", 4265 TALER_B2S (&exchange_pub), 4266 GNUNET_TIME_timestamp2s (start_time), 4267 GNUNET_TIME_relative2s (duration, 4268 false), 4269 legal_end_s); 4270 GNUNET_free (legal_end_s); 4271 } 4272 } 4273 return GNUNET_OK; 4274 } 4275 4276 4277 /** 4278 * Looks up the AGE_RESTRICTED setting for a denomination in the config and 4279 * returns the age restriction (mask) accordingly. 4280 * 4281 * @param section_name Section in the configuration for the particular 4282 * denomination. 4283 */ 4284 static struct TALER_AgeMask 4285 load_age_mask (const char *section_name) 4286 { 4287 static const struct TALER_AgeMask null_mask = {0}; 4288 enum GNUNET_GenericReturnValue ret; 4289 4290 if (GNUNET_YES != 4291 GNUNET_CONFIGURATION_get_value_yesno (kcfg, 4292 "exchange", 4293 "AGE_RESTRICTION_ENABLED")) 4294 return null_mask; 4295 4296 if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value ( 4297 kcfg, 4298 section_name, 4299 "AGE_RESTRICTED"))) 4300 return null_mask; 4301 4302 ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg, 4303 section_name, 4304 "AGE_RESTRICTED"); 4305 if (GNUNET_SYSERR == ret) 4306 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4307 section_name, 4308 "AGE_RESTRICTED", 4309 "Value must be YES or NO\n"); 4310 if (GNUNET_YES == ret) 4311 { 4312 char *groups = NULL; 4313 struct TALER_AgeMask mask = {0}; 4314 4315 if (GNUNET_OK != 4316 GNUNET_CONFIGURATION_get_value_string (kcfg, 4317 "exchange", 4318 "AGE_GROUPS", 4319 &groups)) 4320 groups = GNUNET_strdup ("8:10:12:14:16:18:21"); 4321 if (GNUNET_OK == 4322 TALER_parse_age_group_string (groups, &mask)) 4323 { 4324 GNUNET_free (groups); 4325 return mask; 4326 } 4327 GNUNET_free (groups); 4328 } 4329 4330 return null_mask; 4331 } 4332 4333 4334 /** 4335 * Output @a denomkeys for human consumption. 4336 * 4337 * @param secm_pub_rsa security module public key used to sign the RSA denominations 4338 * @param secm_pub_cs security module public key used to sign the CS denominations 4339 * @param denomkeys keys to output 4340 * @return #GNUNET_OK on success 4341 */ 4342 static enum GNUNET_GenericReturnValue 4343 show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, 4344 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs, 4345 const json_t *denomkeys) 4346 { 4347 size_t index; 4348 json_t *value; 4349 4350 json_array_foreach (denomkeys, index, value) { 4351 const char *err_name; 4352 unsigned int err_line; 4353 const char *section_name; 4354 struct TALER_DenominationPublicKey denom_pub; 4355 struct GNUNET_TIME_Timestamp stamp_start; 4356 struct GNUNET_TIME_Timestamp stamp_expire_withdraw; 4357 struct GNUNET_TIME_Timestamp stamp_expire_deposit; 4358 struct GNUNET_TIME_Timestamp stamp_expire_legal; 4359 struct TALER_Amount coin_value; 4360 struct TALER_DenomFeeSet fees; 4361 struct TALER_SecurityModuleSignatureP secm_sig; 4362 struct GNUNET_JSON_Specification spec[] = { 4363 GNUNET_JSON_spec_string ("section_name", 4364 §ion_name), 4365 TALER_JSON_spec_denom_pub ("denom_pub", 4366 &denom_pub), 4367 TALER_JSON_spec_amount ("value", 4368 currency, 4369 &coin_value), 4370 TALER_JSON_SPEC_DENOM_FEES ("fee", 4371 currency, 4372 &fees), 4373 GNUNET_JSON_spec_timestamp ("stamp_start", 4374 &stamp_start), 4375 GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw", 4376 &stamp_expire_withdraw), 4377 GNUNET_JSON_spec_timestamp ("stamp_expire_deposit", 4378 &stamp_expire_deposit), 4379 GNUNET_JSON_spec_timestamp ("stamp_expire_legal", 4380 &stamp_expire_legal), 4381 GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig", 4382 &secm_sig), 4383 GNUNET_JSON_spec_end () 4384 }; 4385 struct GNUNET_TIME_Relative duration; 4386 struct TALER_DenominationHashP h_denom_pub; 4387 enum GNUNET_GenericReturnValue ok; 4388 4389 if (GNUNET_OK != 4390 GNUNET_JSON_parse (value, 4391 spec, 4392 &err_name, 4393 &err_line)) 4394 { 4395 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4396 "Invalid input for denomination key to 'show': %s#%u at %u (skipping)\n", 4397 err_name, 4398 err_line, 4399 (unsigned int) index); 4400 json_dumpf (value, 4401 stderr, 4402 JSON_INDENT (2)); 4403 GNUNET_JSON_parse_free (spec); 4404 global_ret = EXIT_FAILURE; 4405 GNUNET_SCHEDULER_shutdown (); 4406 return GNUNET_SYSERR; 4407 } 4408 duration = GNUNET_TIME_absolute_get_difference ( 4409 stamp_start.abs_time, 4410 stamp_expire_withdraw.abs_time); 4411 denom_pub.age_mask = load_age_mask (section_name); 4412 TALER_denom_pub_hash (&denom_pub, 4413 &h_denom_pub); 4414 switch (denom_pub.bsign_pub_key->cipher) 4415 { 4416 case GNUNET_CRYPTO_BSA_RSA: 4417 { 4418 struct TALER_RsaPubHashP h_rsa; 4419 4420 TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key, 4421 &h_rsa); 4422 ok = TALER_exchange_secmod_rsa_verify (&h_rsa, 4423 section_name, 4424 stamp_start, 4425 duration, 4426 secm_pub_rsa, 4427 &secm_sig); 4428 } 4429 break; 4430 case GNUNET_CRYPTO_BSA_CS: 4431 { 4432 struct TALER_CsPubHashP h_cs; 4433 4434 TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key, 4435 &h_cs); 4436 ok = TALER_exchange_secmod_cs_verify (&h_cs, 4437 section_name, 4438 stamp_start, 4439 duration, 4440 secm_pub_cs, 4441 &secm_sig); 4442 } 4443 break; 4444 default: 4445 GNUNET_break (0); 4446 ok = GNUNET_SYSERR; 4447 break; 4448 } 4449 if (GNUNET_OK != ok) 4450 { 4451 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4452 "Invalid security module signature for denomination key %s (aborting)\n", 4453 GNUNET_h2s (&h_denom_pub.hash)); 4454 global_ret = EXIT_FAILURE; 4455 GNUNET_JSON_parse_free (spec); 4456 GNUNET_SCHEDULER_shutdown (); 4457 return GNUNET_SYSERR; 4458 } 4459 4460 { 4461 char *withdraw_fee_s; 4462 char *deposit_fee_s; 4463 char *refresh_fee_s; 4464 char *refund_fee_s; 4465 char *deposit_s; 4466 char *legal_s; 4467 4468 withdraw_fee_s = TALER_amount_to_string (&fees.withdraw); 4469 deposit_fee_s = TALER_amount_to_string (&fees.deposit); 4470 refresh_fee_s = TALER_amount_to_string (&fees.refresh); 4471 refund_fee_s = TALER_amount_to_string (&fees.refund); 4472 deposit_s = GNUNET_strdup ( 4473 GNUNET_TIME_timestamp2s (stamp_expire_deposit)); 4474 legal_s = GNUNET_strdup ( 4475 GNUNET_TIME_timestamp2s (stamp_expire_legal)); 4476 4477 printf ( 4478 "DENOMINATION-KEY(%s) %s of value %s starting at %s " 4479 "(used for: %s, deposit until: %s legal end: %s) with fees %s/%s/%s/%s\n", 4480 section_name, 4481 TALER_B2S (&h_denom_pub), 4482 TALER_amount2s (&coin_value), 4483 GNUNET_TIME_timestamp2s (stamp_start), 4484 GNUNET_TIME_relative2s (duration, 4485 false), 4486 deposit_s, 4487 legal_s, 4488 withdraw_fee_s, 4489 deposit_fee_s, 4490 refresh_fee_s, 4491 refund_fee_s); 4492 GNUNET_free (withdraw_fee_s); 4493 GNUNET_free (deposit_fee_s); 4494 GNUNET_free (refresh_fee_s); 4495 GNUNET_free (refund_fee_s); 4496 GNUNET_free (deposit_s); 4497 GNUNET_free (legal_s); 4498 } 4499 4500 GNUNET_JSON_parse_free (spec); 4501 } 4502 return GNUNET_OK; 4503 } 4504 4505 4506 /** 4507 * Parse the input of exchange keys for the 'show' and 'sign' commands. 4508 * 4509 * @param command_name name of the command, for logging 4510 * @return NULL on error, otherwise the keys details to be free'd by caller 4511 */ 4512 static json_t * 4513 parse_keys_input (const char *command_name) 4514 { 4515 const char *op_str; 4516 json_t *keys; 4517 struct GNUNET_JSON_Specification spec[] = { 4518 GNUNET_JSON_spec_json ("arguments", 4519 &keys), 4520 GNUNET_JSON_spec_string ("operation", 4521 &op_str), 4522 GNUNET_JSON_spec_end () 4523 }; 4524 const char *err_name; 4525 unsigned int err_line; 4526 4527 if (NULL == in) 4528 { 4529 json_error_t err; 4530 4531 in = json_loadf (stdin, 4532 JSON_REJECT_DUPLICATES, 4533 &err); 4534 if (NULL == in) 4535 { 4536 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4537 "Failed to read JSON input: %s at %d:%s (offset: %d)\n", 4538 err.text, 4539 err.line, 4540 err.source, 4541 err.position); 4542 global_ret = EXIT_FAILURE; 4543 GNUNET_SCHEDULER_shutdown (); 4544 return NULL; 4545 } 4546 } 4547 if (GNUNET_OK != 4548 GNUNET_JSON_parse (in, 4549 spec, 4550 &err_name, 4551 &err_line)) 4552 { 4553 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4554 "Invalid input to '%s': %s#%u (skipping)\n", 4555 command_name, 4556 err_name, 4557 err_line); 4558 json_dumpf (in, 4559 stderr, 4560 JSON_INDENT (2)); 4561 global_ret = EXIT_FAILURE; 4562 GNUNET_SCHEDULER_shutdown (); 4563 return NULL; 4564 } 4565 if (0 != strcmp (op_str, 4566 OP_INPUT_KEYS)) 4567 { 4568 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4569 "Invalid input to '%s' : operation is `%s', expected `%s'\n", 4570 command_name, 4571 op_str, 4572 OP_INPUT_KEYS); 4573 GNUNET_JSON_parse_free (spec); 4574 return NULL; 4575 } 4576 json_decref (in); 4577 in = NULL; 4578 return keys; 4579 } 4580 4581 4582 /** 4583 * Show future keys. 4584 * 4585 * @param args the array of command-line arguments to process next 4586 */ 4587 static void 4588 do_show (char *const *args) 4589 { 4590 json_t *keys; 4591 const char *err_name; 4592 unsigned int err_line; 4593 const json_t *denomkeys; 4594 const json_t *signkeys; 4595 struct TALER_MasterPublicKeyP mpub; 4596 struct TALER_SecurityModulePublicKeySetP secmset; 4597 struct GNUNET_JSON_Specification spec[] = { 4598 GNUNET_JSON_spec_array_const ("future_denoms", 4599 &denomkeys), 4600 GNUNET_JSON_spec_array_const ("future_signkeys", 4601 &signkeys), 4602 GNUNET_JSON_spec_fixed_auto ("master_pub", 4603 &mpub), 4604 GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key", 4605 &secmset.rsa), 4606 GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key", 4607 &secmset.cs), 4608 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key", 4609 &secmset.eddsa), 4610 GNUNET_JSON_spec_end () 4611 }; 4612 4613 keys = parse_keys_input ("show"); 4614 if (NULL == keys) 4615 return; 4616 4617 if (GNUNET_OK != 4618 load_offline_key (GNUNET_NO)) 4619 { 4620 json_decref (keys); 4621 return; 4622 } 4623 if (GNUNET_OK != 4624 GNUNET_JSON_parse (keys, 4625 spec, 4626 &err_name, 4627 &err_line)) 4628 { 4629 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4630 "Invalid input to 'show': %s #%u (skipping)\n", 4631 err_name, 4632 err_line); 4633 json_dumpf (keys, 4634 stderr, 4635 JSON_INDENT (2)); 4636 global_ret = EXIT_FAILURE; 4637 GNUNET_SCHEDULER_shutdown (); 4638 json_decref (keys); 4639 return; 4640 } 4641 if (0 != 4642 GNUNET_memcmp (&master_pub, 4643 &mpub)) 4644 { 4645 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4646 "Fatal: exchange uses different master key!\n"); 4647 global_ret = EXIT_FAILURE; 4648 GNUNET_SCHEDULER_shutdown (); 4649 json_decref (keys); 4650 return; 4651 } 4652 if (GNUNET_SYSERR == 4653 tofu_check (&secmset)) 4654 { 4655 global_ret = EXIT_FAILURE; 4656 GNUNET_SCHEDULER_shutdown (); 4657 json_decref (keys); 4658 return; 4659 } 4660 if ( (GNUNET_OK != 4661 show_signkeys (&secmset.eddsa, 4662 signkeys)) || 4663 (GNUNET_OK != 4664 show_denomkeys (&secmset.rsa, 4665 &secmset.cs, 4666 denomkeys)) ) 4667 { 4668 global_ret = EXIT_FAILURE; 4669 GNUNET_SCHEDULER_shutdown (); 4670 json_decref (keys); 4671 return; 4672 } 4673 json_decref (keys); 4674 next (args); 4675 } 4676 4677 4678 /** 4679 * Sign @a signkeys with offline key. 4680 * 4681 * @param secm_pub security module public key used to sign the denominations 4682 * @param signkeys keys to output 4683 * @param[in,out] result array where to output the signatures 4684 * @return #GNUNET_OK on success 4685 */ 4686 static enum GNUNET_GenericReturnValue 4687 sign_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub, 4688 const json_t *signkeys, 4689 json_t *result) 4690 { 4691 size_t index; 4692 json_t *value; 4693 4694 json_array_foreach (signkeys, index, value) { 4695 const char *err_name; 4696 unsigned int err_line; 4697 struct TALER_ExchangePublicKeyP exchange_pub; 4698 struct TALER_SecurityModuleSignatureP secm_sig; 4699 struct GNUNET_TIME_Timestamp start_time; 4700 struct GNUNET_TIME_Timestamp sign_end; 4701 struct GNUNET_TIME_Timestamp legal_end; 4702 struct GNUNET_TIME_Relative duration; 4703 struct GNUNET_JSON_Specification spec[] = { 4704 GNUNET_JSON_spec_timestamp ("stamp_start", 4705 &start_time), 4706 GNUNET_JSON_spec_timestamp ("stamp_expire", 4707 &sign_end), 4708 GNUNET_JSON_spec_timestamp ("stamp_end", 4709 &legal_end), 4710 GNUNET_JSON_spec_fixed_auto ("key", 4711 &exchange_pub), 4712 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig", 4713 &secm_sig), 4714 GNUNET_JSON_spec_end () 4715 }; 4716 4717 if (GNUNET_OK != 4718 GNUNET_JSON_parse (value, 4719 spec, 4720 &err_name, 4721 &err_line)) 4722 { 4723 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4724 "Invalid input for signing key to 'show': %s #%u at %u (skipping)\n", 4725 err_name, 4726 err_line, 4727 (unsigned int) index); 4728 json_dumpf (value, 4729 stderr, 4730 JSON_INDENT (2)); 4731 global_ret = EXIT_FAILURE; 4732 GNUNET_SCHEDULER_shutdown (); 4733 return GNUNET_SYSERR; 4734 } 4735 4736 duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time, 4737 sign_end.abs_time); 4738 if (GNUNET_OK != 4739 TALER_exchange_secmod_eddsa_verify (&exchange_pub, 4740 start_time, 4741 duration, 4742 secm_pub, 4743 &secm_sig)) 4744 { 4745 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4746 "Invalid security module signature for signing key %s (aborting)\n", 4747 TALER_B2S (&exchange_pub)); 4748 global_ret = EXIT_FAILURE; 4749 GNUNET_SCHEDULER_shutdown (); 4750 GNUNET_JSON_parse_free (spec); 4751 return GNUNET_SYSERR; 4752 } 4753 { 4754 struct TALER_MasterSignatureP master_sig; 4755 4756 TALER_exchange_offline_signkey_validity_sign (&exchange_pub, 4757 start_time, 4758 sign_end, 4759 legal_end, 4760 &master_priv, 4761 &master_sig); 4762 GNUNET_assert (0 == 4763 json_array_append_new ( 4764 result, 4765 GNUNET_JSON_PACK ( 4766 GNUNET_JSON_pack_data_auto ("exchange_pub", 4767 &exchange_pub), 4768 GNUNET_JSON_pack_data_auto ("master_sig", 4769 &master_sig)))); 4770 } 4771 GNUNET_JSON_parse_free (spec); 4772 } 4773 return GNUNET_OK; 4774 } 4775 4776 4777 /** 4778 * Sign @a denomkeys with offline key. 4779 * 4780 * @param secm_pub_rsa security module public key used to sign the RSA denominations 4781 * @param secm_pub_cs security module public key used to sign the CS denominations 4782 * @param denomkeys keys to output 4783 * @param[in,out] result array where to output the signatures 4784 * @return #GNUNET_OK on success 4785 */ 4786 static enum GNUNET_GenericReturnValue 4787 sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, 4788 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs, 4789 const json_t *denomkeys, 4790 json_t *result) 4791 { 4792 size_t index; 4793 json_t *value; 4794 4795 json_array_foreach (denomkeys, index, value) { 4796 const char *err_name; 4797 unsigned int err_line; 4798 const char *section_name; 4799 struct TALER_DenominationPublicKey denom_pub; 4800 struct GNUNET_TIME_Timestamp stamp_start; 4801 struct GNUNET_TIME_Timestamp stamp_expire_withdraw; 4802 struct GNUNET_TIME_Timestamp stamp_expire_deposit; 4803 struct GNUNET_TIME_Timestamp stamp_expire_legal; 4804 struct TALER_Amount coin_value; 4805 struct TALER_DenomFeeSet fees; 4806 struct TALER_SecurityModuleSignatureP secm_sig; 4807 struct GNUNET_JSON_Specification spec[] = { 4808 GNUNET_JSON_spec_string ("section_name", 4809 §ion_name), 4810 TALER_JSON_spec_denom_pub ("denom_pub", 4811 &denom_pub), 4812 TALER_JSON_spec_amount ("value", 4813 currency, 4814 &coin_value), 4815 TALER_JSON_SPEC_DENOM_FEES ("fee", 4816 currency, 4817 &fees), 4818 GNUNET_JSON_spec_timestamp ("stamp_start", 4819 &stamp_start), 4820 GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw", 4821 &stamp_expire_withdraw), 4822 GNUNET_JSON_spec_timestamp ("stamp_expire_deposit", 4823 &stamp_expire_deposit), 4824 GNUNET_JSON_spec_timestamp ("stamp_expire_legal", 4825 &stamp_expire_legal), 4826 GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig", 4827 &secm_sig), 4828 GNUNET_JSON_spec_end () 4829 }; 4830 struct GNUNET_TIME_Relative duration; 4831 struct TALER_DenominationHashP h_denom_pub; 4832 4833 if (GNUNET_OK != 4834 GNUNET_JSON_parse (value, 4835 spec, 4836 &err_name, 4837 &err_line)) 4838 { 4839 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4840 "Invalid input for denomination key to 'sign': %s #%u at %u (skipping)\n", 4841 err_name, 4842 err_line, 4843 (unsigned int) index); 4844 json_dumpf (value, 4845 stderr, 4846 JSON_INDENT (2)); 4847 GNUNET_JSON_parse_free (spec); 4848 global_ret = EXIT_FAILURE; 4849 GNUNET_SCHEDULER_shutdown (); 4850 return GNUNET_SYSERR; 4851 } 4852 duration = GNUNET_TIME_absolute_get_difference ( 4853 stamp_start.abs_time, 4854 stamp_expire_withdraw.abs_time); 4855 4856 /* Load the age mask, if applicable to this denomination */ 4857 denom_pub.age_mask = load_age_mask (section_name); 4858 4859 TALER_denom_pub_hash (&denom_pub, 4860 &h_denom_pub); 4861 4862 switch (denom_pub.bsign_pub_key->cipher) 4863 { 4864 case GNUNET_CRYPTO_BSA_RSA: 4865 { 4866 struct TALER_RsaPubHashP h_rsa; 4867 4868 TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key, 4869 &h_rsa); 4870 if (GNUNET_OK != 4871 TALER_exchange_secmod_rsa_verify (&h_rsa, 4872 section_name, 4873 stamp_start, 4874 duration, 4875 secm_pub_rsa, 4876 &secm_sig)) 4877 { 4878 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4879 "Invalid security module signature for denomination key %s (aborting)\n", 4880 GNUNET_h2s (&h_denom_pub.hash)); 4881 global_ret = EXIT_FAILURE; 4882 GNUNET_SCHEDULER_shutdown (); 4883 GNUNET_JSON_parse_free (spec); 4884 return GNUNET_SYSERR; 4885 } 4886 } 4887 break; 4888 case GNUNET_CRYPTO_BSA_CS: 4889 { 4890 struct TALER_CsPubHashP h_cs; 4891 4892 TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key, 4893 &h_cs); 4894 if (GNUNET_OK != 4895 TALER_exchange_secmod_cs_verify (&h_cs, 4896 section_name, 4897 stamp_start, 4898 duration, 4899 secm_pub_cs, 4900 &secm_sig)) 4901 { 4902 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4903 "Invalid security module signature for denomination key %s (aborting)\n", 4904 GNUNET_h2s (&h_denom_pub.hash)); 4905 global_ret = EXIT_FAILURE; 4906 GNUNET_SCHEDULER_shutdown (); 4907 GNUNET_JSON_parse_free (spec); 4908 return GNUNET_SYSERR; 4909 } 4910 } 4911 break; 4912 default: 4913 global_ret = EXIT_FAILURE; 4914 GNUNET_SCHEDULER_shutdown (); 4915 GNUNET_JSON_parse_free (spec); 4916 return GNUNET_SYSERR; 4917 } 4918 4919 { 4920 struct TALER_MasterSignatureP master_sig; 4921 4922 TALER_exchange_offline_denom_validity_sign (&h_denom_pub, 4923 stamp_start, 4924 stamp_expire_withdraw, 4925 stamp_expire_deposit, 4926 stamp_expire_legal, 4927 &coin_value, 4928 &fees, 4929 &master_priv, 4930 &master_sig); 4931 GNUNET_assert (0 == 4932 json_array_append_new ( 4933 result, 4934 GNUNET_JSON_PACK ( 4935 GNUNET_JSON_pack_data_auto ("h_denom_pub", 4936 &h_denom_pub), 4937 GNUNET_JSON_pack_data_auto ("master_sig", 4938 &master_sig)))); 4939 } 4940 GNUNET_JSON_parse_free (spec); 4941 } 4942 return GNUNET_OK; 4943 } 4944 4945 4946 /** 4947 * Sign future keys. 4948 * 4949 * @param args the array of command-line arguments to process next 4950 */ 4951 static void 4952 do_sign (char *const *args) 4953 { 4954 json_t *keys; 4955 const char *err_name; 4956 unsigned int err_line; 4957 const json_t *denomkeys; 4958 const json_t *signkeys; 4959 struct TALER_MasterPublicKeyP mpub; 4960 struct TALER_SecurityModulePublicKeySetP secmset; 4961 struct GNUNET_JSON_Specification spec[] = { 4962 GNUNET_JSON_spec_array_const ("future_denoms", 4963 &denomkeys), 4964 GNUNET_JSON_spec_array_const ("future_signkeys", 4965 &signkeys), 4966 GNUNET_JSON_spec_fixed_auto ("master_pub", 4967 &mpub), 4968 GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key", 4969 &secmset.rsa), 4970 GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key", 4971 &secmset.cs), 4972 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key", 4973 &secmset.eddsa), 4974 GNUNET_JSON_spec_end () 4975 }; 4976 4977 keys = parse_keys_input ("sign"); 4978 if (NULL == keys) 4979 return; 4980 if (GNUNET_OK != 4981 load_offline_key (GNUNET_NO)) 4982 { 4983 json_decref (keys); 4984 return; 4985 } 4986 if (GNUNET_OK != 4987 GNUNET_JSON_parse (keys, 4988 spec, 4989 &err_name, 4990 &err_line)) 4991 { 4992 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4993 "Invalid input to 'sign' : %s #%u (skipping)\n", 4994 err_name, 4995 err_line); 4996 json_dumpf (keys, 4997 stderr, 4998 JSON_INDENT (2)); 4999 global_ret = EXIT_FAILURE; 5000 GNUNET_SCHEDULER_shutdown (); 5001 json_decref (keys); 5002 return; 5003 } 5004 if (0 != 5005 GNUNET_memcmp (&master_pub, 5006 &mpub)) 5007 { 5008 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5009 "Fatal: exchange uses different master key!\n"); 5010 global_ret = EXIT_FAILURE; 5011 GNUNET_SCHEDULER_shutdown (); 5012 json_decref (keys); 5013 return; 5014 } 5015 if (GNUNET_SYSERR == 5016 tofu_check (&secmset)) 5017 { 5018 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5019 "Fatal: security module keys changed!\n"); 5020 global_ret = EXIT_FAILURE; 5021 GNUNET_SCHEDULER_shutdown (); 5022 json_decref (keys); 5023 return; 5024 } 5025 { 5026 json_t *signkey_sig_array = json_array (); 5027 json_t *denomkey_sig_array = json_array (); 5028 5029 GNUNET_assert (NULL != signkey_sig_array); 5030 GNUNET_assert (NULL != denomkey_sig_array); 5031 if ( (GNUNET_OK != 5032 sign_signkeys (&secmset.eddsa, 5033 signkeys, 5034 signkey_sig_array)) || 5035 (GNUNET_OK != 5036 sign_denomkeys (&secmset.rsa, 5037 &secmset.cs, 5038 denomkeys, 5039 denomkey_sig_array)) ) 5040 { 5041 global_ret = EXIT_FAILURE; 5042 GNUNET_SCHEDULER_shutdown (); 5043 json_decref (signkey_sig_array); 5044 json_decref (denomkey_sig_array); 5045 json_decref (keys); 5046 return; 5047 } 5048 5049 output_operation (OP_UPLOAD_SIGS, 5050 GNUNET_JSON_PACK ( 5051 GNUNET_JSON_pack_array_steal ("denom_sigs", 5052 denomkey_sig_array), 5053 GNUNET_JSON_pack_array_steal ("signkey_sigs", 5054 signkey_sig_array))); 5055 } 5056 json_decref (keys); 5057 next (args); 5058 } 5059 5060 5061 /** 5062 * Setup and output offline signing key. 5063 * 5064 * @param args the array of command-line arguments to process next 5065 */ 5066 static void 5067 do_setup (char *const *args) 5068 { 5069 if (GNUNET_OK != 5070 load_offline_key (GNUNET_YES)) 5071 { 5072 global_ret = EXIT_NOPERMISSION; 5073 return; 5074 } 5075 if (NULL != *args) 5076 { 5077 output_operation (OP_SETUP, 5078 GNUNET_JSON_PACK ( 5079 GNUNET_JSON_pack_data_auto ("exchange_offline_pub", 5080 &master_pub))); 5081 } 5082 5083 else 5084 { 5085 char *pub_s; 5086 5087 pub_s = GNUNET_STRINGS_data_to_string_alloc (&master_pub, 5088 sizeof (master_pub)); 5089 fprintf (stdout, 5090 "%s\n", 5091 pub_s); 5092 GNUNET_free (pub_s); 5093 } 5094 if ( (NULL != *args) && 5095 (0 == strcmp (*args, 5096 "-")) ) 5097 args++; 5098 next (args); 5099 } 5100 5101 5102 /** 5103 * Dispatch @a args in the @a cmds array. 5104 * 5105 * @param args arguments with subcommand to dispatch 5106 * @param cmds array of possible subcommands to call 5107 */ 5108 static void 5109 cmd_handler (char *const *args, 5110 const struct SubCommand *cmds) 5111 { 5112 nxt = NULL; 5113 for (unsigned int i = 0; NULL != cmds[i].name; i++) 5114 { 5115 if (0 == strcasecmp (cmds[i].name, 5116 args[0])) 5117 { 5118 cmds[i].cb (&args[1]); 5119 return; 5120 } 5121 } 5122 5123 if (0 != strcasecmp ("help", 5124 args[0])) 5125 { 5126 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5127 "Unexpected command `%s'\n", 5128 args[0]); 5129 global_ret = EXIT_INVALIDARGUMENT; 5130 } 5131 5132 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5133 "Supported subcommands:\n"); 5134 for (unsigned int i = 0; NULL != cmds[i].name; i++) 5135 { 5136 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5137 "- %s: %s\n", 5138 cmds[i].name, 5139 cmds[i].help); 5140 } 5141 json_decref (out); 5142 out = NULL; 5143 GNUNET_SCHEDULER_shutdown (); 5144 } 5145 5146 5147 static void 5148 work (void *cls) 5149 { 5150 char *const *args = cls; 5151 struct SubCommand cmds[] = { 5152 { 5153 .name = "setup", 5154 .help = 5155 "initialize offline key signing material and display public offline key", 5156 .cb = &do_setup 5157 }, 5158 { 5159 .name = "download", 5160 .help = 5161 "obtain future public keys from exchange (to be performed online!)", 5162 .cb = &do_download 5163 }, 5164 { 5165 .name = "show", 5166 .help = 5167 "display future public keys from exchange for human review (pass '-' as argument to disable consuming input)", 5168 .cb = &do_show 5169 }, 5170 { 5171 .name = "sign", 5172 .help = "sign all future public keys from the input", 5173 .cb = &do_sign 5174 }, 5175 { 5176 .name = "revoke-denomination", 5177 .help = 5178 "revoke denomination key (hash of public key must be given as argument)", 5179 .cb = &do_revoke_denomination_key 5180 }, 5181 { 5182 .name = "revoke-signkey", 5183 .help = 5184 "revoke exchange online signing key (public key must be given as argument)", 5185 .cb = &do_revoke_signkey 5186 }, 5187 { 5188 .name = "enable-auditor", 5189 .help = 5190 "enable auditor for the exchange (auditor-public key, auditor-URI and auditor-name must be given as arguments)", 5191 .cb = &do_add_auditor 5192 }, 5193 { 5194 .name = "disable-auditor", 5195 .help = 5196 "disable auditor at the exchange (auditor-public key must be given as argument)", 5197 .cb = &do_del_auditor 5198 }, 5199 { 5200 .name = "enable-account", 5201 .help = 5202 "enable wire account of the exchange (payto-URI must be given as argument; for optional arguments see man page)", 5203 .cb = &do_add_wire 5204 }, 5205 { 5206 .name = "disable-account", 5207 .help = 5208 "disable wire account of the exchange (payto-URI must be given as argument)", 5209 .cb = &do_del_wire 5210 }, 5211 { 5212 .name = "wire-fee", 5213 .help = 5214 "sign wire fees for the given year (year, wire method, wire fee, and closing fee must be given as arguments)", 5215 .cb = &do_set_wire_fee 5216 }, 5217 { 5218 .name = "global-fee", 5219 .help = 5220 "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)", 5221 .cb = &do_set_global_fee 5222 }, 5223 { 5224 .name = "drain", 5225 .help = 5226 "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)", 5227 .cb = &do_drain 5228 }, 5229 { 5230 .name = "add-partner", 5231 .help = 5232 "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)", 5233 .cb = &do_add_partner 5234 }, 5235 { 5236 .name = "aml-enable", 5237 .help = 5238 "enable AML staff member (staff member public key, legal name and rw (read write) or ro (read only) must be given as arguments)", 5239 .cb = &enable_aml_staff 5240 }, 5241 { 5242 .name = "aml-disable", 5243 .help = 5244 "disable AML staff member (staff member public key and legal name must be given as arguments)", 5245 .cb = &disable_aml_staff 5246 }, 5247 { 5248 .name = "upload", 5249 .help = 5250 "upload operation result to exchange (to be performed online!)", 5251 .cb = &do_upload 5252 }, 5253 /* list terminator */ 5254 { 5255 .name = NULL, 5256 } 5257 }; 5258 (void) cls; 5259 5260 cmd_handler (args, cmds); 5261 } 5262 5263 5264 /** 5265 * Main function that will be run. 5266 * 5267 * @param cls closure 5268 * @param args remaining command-line arguments 5269 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 5270 * @param cfg configuration 5271 */ 5272 static void 5273 run (void *cls, 5274 char *const *args, 5275 const char *cfgfile, 5276 const struct GNUNET_CONFIGURATION_Handle *cfg) 5277 { 5278 (void) cls; 5279 (void) cfgfile; 5280 kcfg = cfg; 5281 5282 /* age restriction is read per-denomination in load_age_mask() from [exchange] */ 5283 5284 5285 if (GNUNET_OK != 5286 TALER_config_get_currency (kcfg, 5287 "exchange", 5288 ¤cy)) 5289 { 5290 global_ret = EXIT_NOTCONFIGURED; 5291 return; 5292 } 5293 5294 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 5295 &rc); 5296 rc = GNUNET_CURL_gnunet_rc_create (ctx); 5297 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 5298 NULL); 5299 next (args); 5300 } 5301 5302 5303 /** 5304 * The main function of the taler-exchange-offline tool. This tool is used to 5305 * create the signing and denomination keys for the exchange. It uses the 5306 * long-term offline private key and generates signatures with it. It also 5307 * supports online operations with the exchange to download its input data and 5308 * to upload its results. Those online operations should be performed on 5309 * another machine in production! 5310 * 5311 * @param argc number of arguments from the command line 5312 * @param argv command line arguments 5313 * @return 0 ok, 1 on error 5314 */ 5315 int 5316 main (int argc, 5317 char *const *argv) 5318 { 5319 struct GNUNET_GETOPT_CommandLineOption options[] = { 5320 GNUNET_GETOPT_OPTION_END 5321 }; 5322 enum GNUNET_GenericReturnValue ret; 5323 5324 ret = GNUNET_PROGRAM_run ( 5325 TALER_EXCHANGE_project_data (), 5326 argc, argv, 5327 "taler-exchange-offline", 5328 gettext_noop ("Operations for offline signing for a Taler exchange"), 5329 options, 5330 &run, NULL); 5331 if (GNUNET_SYSERR == ret) 5332 return EXIT_INVALIDARGUMENT; 5333 if (GNUNET_NO == ret) 5334 return EXIT_SUCCESS; 5335 return global_ret; 5336 } 5337 5338 5339 /* end of taler-exchange-offline.c */