exchange_api_common.c (39213B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2015-2023 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 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file lib/exchange_api_common.c 19 * @brief common functions for the exchange API 20 * @author Christian Grothoff 21 */ 22 #include "taler/taler_json_lib.h" 23 #include <microhttpd.h> 24 #include <gnunet/gnunet_curl_lib.h> 25 #include "exchange_api_common.h" 26 #include "exchange_api_handle.h" 27 #include "taler/taler_signatures.h" 28 29 30 const struct TALER_EXCHANGE_SigningPublicKey * 31 TALER_EXCHANGE_get_signing_key_info ( 32 const struct TALER_EXCHANGE_Keys *keys, 33 const struct TALER_ExchangePublicKeyP *exchange_pub) 34 { 35 for (unsigned int i = 0; i<keys->num_sign_keys; i++) 36 { 37 const struct TALER_EXCHANGE_SigningPublicKey *spk 38 = &keys->sign_keys[i]; 39 40 if (0 == GNUNET_memcmp (exchange_pub, 41 &spk->key)) 42 return spk; 43 } 44 return NULL; 45 } 46 47 48 enum GNUNET_GenericReturnValue 49 TALER_EXCHANGE_check_purse_create_conflict_ ( 50 const struct TALER_PurseContractSignatureP *cpurse_sig, 51 const struct TALER_PurseContractPublicKeyP *purse_pub, 52 const json_t *proof) 53 { 54 struct TALER_Amount amount; 55 uint32_t min_age; 56 struct GNUNET_TIME_Timestamp purse_expiration; 57 struct TALER_PurseContractSignatureP purse_sig; 58 struct TALER_PrivateContractHashP h_contract_terms; 59 struct TALER_PurseMergePublicKeyP merge_pub; 60 struct GNUNET_JSON_Specification spec[] = { 61 TALER_JSON_spec_amount_any ("amount", 62 &amount), 63 GNUNET_JSON_spec_uint32 ("min_age", 64 &min_age), 65 GNUNET_JSON_spec_timestamp ("purse_expiration", 66 &purse_expiration), 67 GNUNET_JSON_spec_fixed_auto ("purse_sig", 68 &purse_sig), 69 GNUNET_JSON_spec_fixed_auto ("h_contract_terms", 70 &h_contract_terms), 71 GNUNET_JSON_spec_fixed_auto ("merge_pub", 72 &merge_pub), 73 GNUNET_JSON_spec_end () 74 }; 75 76 if (GNUNET_OK != 77 GNUNET_JSON_parse (proof, 78 spec, 79 NULL, NULL)) 80 { 81 GNUNET_break_op (0); 82 return GNUNET_SYSERR; 83 } 84 if (GNUNET_OK != 85 TALER_wallet_purse_create_verify (purse_expiration, 86 &h_contract_terms, 87 &merge_pub, 88 min_age, 89 &amount, 90 purse_pub, 91 &purse_sig)) 92 { 93 GNUNET_break_op (0); 94 return GNUNET_SYSERR; 95 } 96 if (0 == 97 GNUNET_memcmp (&purse_sig, 98 cpurse_sig)) 99 { 100 /* Must be the SAME data, not a conflict! */ 101 GNUNET_break_op (0); 102 return GNUNET_SYSERR; 103 } 104 return GNUNET_OK; 105 } 106 107 108 enum GNUNET_GenericReturnValue 109 TALER_EXCHANGE_check_purse_merge_conflict_ ( 110 const struct TALER_PurseMergeSignatureP *cmerge_sig, 111 const struct TALER_PurseMergePublicKeyP *merge_pub, 112 const struct TALER_PurseContractPublicKeyP *purse_pub, 113 const char *exchange_url, 114 const json_t *proof) 115 { 116 struct TALER_PurseMergeSignatureP merge_sig; 117 struct GNUNET_TIME_Timestamp merge_timestamp; 118 const char *partner_url = NULL; 119 struct TALER_ReservePublicKeyP reserve_pub; 120 struct GNUNET_JSON_Specification spec[] = { 121 GNUNET_JSON_spec_mark_optional ( 122 TALER_JSON_spec_web_url ("partner_url", 123 &partner_url), 124 NULL), 125 GNUNET_JSON_spec_timestamp ("merge_timestamp", 126 &merge_timestamp), 127 GNUNET_JSON_spec_fixed_auto ("merge_sig", 128 &merge_sig), 129 GNUNET_JSON_spec_fixed_auto ("reserve_pub", 130 &reserve_pub), 131 GNUNET_JSON_spec_end () 132 }; 133 struct TALER_NormalizedPayto payto_uri; 134 135 if (GNUNET_OK != 136 GNUNET_JSON_parse (proof, 137 spec, 138 NULL, NULL)) 139 { 140 GNUNET_break_op (0); 141 return GNUNET_SYSERR; 142 } 143 if (NULL == partner_url) 144 partner_url = exchange_url; 145 payto_uri = TALER_reserve_make_payto (partner_url, 146 &reserve_pub); 147 if (GNUNET_OK != 148 TALER_wallet_purse_merge_verify ( 149 payto_uri, 150 merge_timestamp, 151 purse_pub, 152 merge_pub, 153 &merge_sig)) 154 { 155 GNUNET_break_op (0); 156 GNUNET_free (payto_uri.normalized_payto); 157 return GNUNET_SYSERR; 158 } 159 GNUNET_free (payto_uri.normalized_payto); 160 if (0 == 161 GNUNET_memcmp (&merge_sig, 162 cmerge_sig)) 163 { 164 /* Must be the SAME data, not a conflict! */ 165 GNUNET_break_op (0); 166 return GNUNET_SYSERR; 167 } 168 return GNUNET_OK; 169 } 170 171 172 enum GNUNET_GenericReturnValue 173 TALER_EXCHANGE_check_purse_coin_conflict_ ( 174 const struct TALER_PurseContractPublicKeyP *purse_pub, 175 const char *exchange_url, 176 const json_t *proof, 177 struct TALER_DenominationHashP *h_denom_pub, 178 struct TALER_AgeCommitmentHashP *phac, 179 struct TALER_CoinSpendPublicKeyP *coin_pub, 180 struct TALER_CoinSpendSignatureP *coin_sig) 181 { 182 const char *partner_url = NULL; 183 struct TALER_Amount amount; 184 struct GNUNET_JSON_Specification spec[] = { 185 GNUNET_JSON_spec_fixed_auto ("h_denom_pub", 186 h_denom_pub), 187 GNUNET_JSON_spec_fixed_auto ("h_age_commitment", 188 phac), 189 GNUNET_JSON_spec_fixed_auto ("coin_sig", 190 coin_sig), 191 GNUNET_JSON_spec_fixed_auto ("coin_pub", 192 coin_pub), 193 GNUNET_JSON_spec_mark_optional ( 194 TALER_JSON_spec_web_url ("partner_url", 195 &partner_url), 196 NULL), 197 TALER_JSON_spec_amount_any ("amount", 198 &amount), 199 GNUNET_JSON_spec_end () 200 }; 201 202 if (GNUNET_OK != 203 GNUNET_JSON_parse (proof, 204 spec, 205 NULL, NULL)) 206 { 207 GNUNET_break_op (0); 208 return GNUNET_SYSERR; 209 } 210 if (NULL == partner_url) 211 partner_url = exchange_url; 212 if (GNUNET_OK != 213 TALER_wallet_purse_deposit_verify ( 214 partner_url, 215 purse_pub, 216 &amount, 217 h_denom_pub, 218 phac, 219 coin_pub, 220 coin_sig)) 221 { 222 GNUNET_break_op (0); 223 return GNUNET_SYSERR; 224 } 225 return GNUNET_OK; 226 } 227 228 229 enum GNUNET_GenericReturnValue 230 TALER_EXCHANGE_check_purse_econtract_conflict_ ( 231 const struct TALER_PurseContractSignatureP *ccontract_sig, 232 const struct TALER_PurseContractPublicKeyP *purse_pub, 233 const json_t *proof) 234 { 235 struct TALER_ContractDiffiePublicP contract_pub; 236 struct TALER_PurseContractSignatureP contract_sig; 237 struct GNUNET_HashCode h_econtract; 238 struct GNUNET_JSON_Specification spec[] = { 239 GNUNET_JSON_spec_fixed_auto ("h_econtract", 240 &h_econtract), 241 GNUNET_JSON_spec_fixed_auto ("econtract_sig", 242 &contract_sig), 243 GNUNET_JSON_spec_fixed_auto ("contract_pub", 244 &contract_pub), 245 GNUNET_JSON_spec_end () 246 }; 247 248 if (GNUNET_OK != 249 GNUNET_JSON_parse (proof, 250 spec, 251 NULL, NULL)) 252 { 253 GNUNET_break_op (0); 254 return GNUNET_SYSERR; 255 } 256 if (GNUNET_OK != 257 TALER_wallet_econtract_upload_verify2 ( 258 &h_econtract, 259 &contract_pub, 260 purse_pub, 261 &contract_sig)) 262 { 263 GNUNET_break_op (0); 264 return GNUNET_SYSERR; 265 } 266 if (0 == 267 GNUNET_memcmp (&contract_sig, 268 ccontract_sig)) 269 { 270 /* Must be the SAME data, not a conflict! */ 271 GNUNET_break_op (0); 272 return GNUNET_SYSERR; 273 } 274 return GNUNET_OK; 275 } 276 277 278 enum GNUNET_GenericReturnValue 279 TALER_EXCHANGE_parse_coin_denomination_conflict_ ( 280 const json_t *proof, 281 struct TALER_EXCHANGE_CoinDenominationConflict *cdc) 282 { 283 struct GNUNET_JSON_Specification spec[] = { 284 GNUNET_JSON_spec_fixed_auto ("coin_pub", 285 &cdc->coin_pub), 286 TALER_JSON_spec_denom_pub ("prev_denom_pub", 287 &cdc->prev_denom_pub), 288 TALER_JSON_spec_denom_sig ("prev_denom_sig", 289 &cdc->prev_denom_sig), 290 GNUNET_JSON_spec_mark_optional ( 291 GNUNET_JSON_spec_fixed_auto ("prev_h_age_commitment", 292 &cdc->prev_h_age_commitment), 293 &cdc->no_prev_age_commitment), 294 GNUNET_JSON_spec_end () 295 }; 296 297 memset (cdc, 298 0, 299 sizeof (*cdc)); 300 if (GNUNET_OK != 301 GNUNET_JSON_parse (proof, 302 spec, 303 NULL, NULL)) 304 { 305 GNUNET_break_op (0); 306 return GNUNET_SYSERR; 307 } 308 TALER_denom_pub_hash (&cdc->prev_denom_pub, 309 &cdc->prev_h_denom_pub); 310 cdc->verified = false; 311 return GNUNET_OK; 312 } 313 314 315 enum GNUNET_GenericReturnValue 316 TALER_EXCHANGE_check_coin_denomination_conflict_ ( 317 const struct TALER_EXCHANGE_Keys *keys, 318 const struct TALER_DenominationHashP *h_denom_pub, 319 struct TALER_EXCHANGE_CoinDenominationConflict *cdc) 320 { 321 const struct TALER_EXCHANGE_DenomPublicKey *dk; 322 struct TALER_CoinPublicInfo cpi = { 323 .coin_pub = cdc->coin_pub, 324 .denom_pub_hash = cdc->prev_h_denom_pub, 325 .h_age_commitment = cdc->prev_h_age_commitment, 326 .no_age_commitment = cdc->no_prev_age_commitment, 327 .denom_sig = cdc->prev_denom_sig 328 }; 329 330 cdc->verified = false; 331 if (0 == 332 GNUNET_memcmp (h_denom_pub, 333 &cdc->prev_h_denom_pub)) 334 { 335 /* Must be a DIFFERENT denomination, not a conflict! */ 336 GNUNET_break_op (0); 337 return GNUNET_SYSERR; 338 } 339 dk = TALER_EXCHANGE_get_denomination_key_by_hash (keys, 340 &cdc->prev_h_denom_pub); 341 if (NULL == dk) 342 { 343 /* TODO[oec]: the exchange names a denomination that is not (or no 344 longer) in our /keys, for example one that expired. We cannot 345 verify the signature, but we also cannot tell that the proof is 346 wrong. We accept the response as unverifiable and leave the 347 decision to the application (see @e verified). */ 348 return GNUNET_NO; 349 } 350 /* the age commitment hash must be present exactly if the 351 denomination is age-restricted */ 352 if (cdc->no_prev_age_commitment != 353 (0 == dk->key.age_mask.bits)) 354 { 355 GNUNET_break_op (0); 356 return GNUNET_SYSERR; 357 } 358 if (0 != 359 TALER_denom_pub_cmp (&dk->key, 360 &cdc->prev_denom_pub)) 361 { 362 /* hash matches, key does not?! */ 363 GNUNET_break_op (0); 364 return GNUNET_SYSERR; 365 } 366 if (GNUNET_OK != 367 TALER_test_coin_valid (&cpi, 368 &cdc->prev_denom_pub)) 369 { 370 GNUNET_break_op (0); 371 return GNUNET_SYSERR; 372 } 373 cdc->verified = true; 374 return GNUNET_OK; 375 } 376 377 378 void 379 TALER_EXCHANGE_free_coin_denomination_conflict_ ( 380 struct TALER_EXCHANGE_CoinDenominationConflict *cdc) 381 { 382 TALER_denom_pub_free (&cdc->prev_denom_pub); 383 TALER_denom_sig_free (&cdc->prev_denom_sig); 384 } 385 386 387 enum GNUNET_GenericReturnValue 388 TALER_EXCHANGE_parse_coin_age_commitment_conflict_ ( 389 const json_t *proof, 390 struct TALER_EXCHANGE_CoinAgeCommitmentConflict *cac) 391 { 392 struct GNUNET_JSON_Specification spec[] = { 393 GNUNET_JSON_spec_fixed_auto ("coin_pub", 394 &cac->coin_pub), 395 GNUNET_JSON_spec_fixed_auto ("h_denom_pub", 396 &cac->h_denom_pub), 397 GNUNET_JSON_spec_mark_optional ( 398 GNUNET_JSON_spec_fixed_auto ("expected_age_commitment_hash", 399 &cac->expected_age_commitment_hash), 400 &cac->no_expected_age_commitment), 401 GNUNET_JSON_spec_string ("conflict_detail", 402 &cac->conflict_detail), 403 TALER_JSON_spec_denom_sig ("prev_denom_sig", 404 &cac->prev_denom_sig), 405 GNUNET_JSON_spec_end () 406 }; 407 408 memset (cac, 409 0, 410 sizeof (*cac)); 411 if (GNUNET_OK != 412 GNUNET_JSON_parse (proof, 413 spec, 414 NULL, NULL)) 415 { 416 GNUNET_break_op (0); 417 return GNUNET_SYSERR; 418 } 419 cac->verified = false; 420 return GNUNET_OK; 421 } 422 423 424 enum GNUNET_GenericReturnValue 425 TALER_EXCHANGE_check_coin_age_commitment_conflict_ ( 426 const struct TALER_EXCHANGE_Keys *keys, 427 const struct TALER_DenominationHashP *h_denom_pub, 428 const struct TALER_AgeCommitmentHashP *h_age_commitment, 429 struct TALER_EXCHANGE_CoinAgeCommitmentConflict *cac) 430 { 431 const struct TALER_EXCHANGE_DenomPublicKey *dk; 432 struct TALER_CoinPublicInfo cpi = { 433 .coin_pub = cac->coin_pub, 434 .denom_pub_hash = cac->h_denom_pub, 435 .h_age_commitment = cac->expected_age_commitment_hash, 436 .no_age_commitment = cac->no_expected_age_commitment, 437 .denom_sig = cac->prev_denom_sig 438 }; 439 440 cac->verified = false; 441 if (0 != 442 GNUNET_memcmp (h_denom_pub, 443 &cac->h_denom_pub)) 444 { 445 /* an age conflict is about the SAME denomination */ 446 GNUNET_break_op (0); 447 return GNUNET_SYSERR; 448 } 449 if (cac->no_expected_age_commitment == (NULL == h_age_commitment)) 450 { 451 /* both absent, or both present: then they must differ */ 452 if ( (cac->no_expected_age_commitment) || 453 (0 == 454 GNUNET_memcmp (h_age_commitment, 455 &cac->expected_age_commitment_hash)) ) 456 { 457 /* Must be a DIFFERENT age commitment, not a conflict! */ 458 GNUNET_break_op (0); 459 return GNUNET_SYSERR; 460 } 461 } 462 dk = TALER_EXCHANGE_get_denomination_key_by_hash (keys, 463 &cac->h_denom_pub); 464 if (NULL == dk) 465 { 466 /* TODO[oec]: as for the denomination conflict, the denomination is 467 not in our /keys, so the signature cannot be checked; accept the 468 response as unverifiable. */ 469 return GNUNET_NO; 470 } 471 if (GNUNET_OK != 472 TALER_test_coin_valid (&cpi, 473 &dk->key)) 474 { 475 GNUNET_break_op (0); 476 return GNUNET_SYSERR; 477 } 478 cac->verified = true; 479 return GNUNET_OK; 480 } 481 482 483 void 484 TALER_EXCHANGE_free_coin_age_commitment_conflict_ ( 485 struct TALER_EXCHANGE_CoinAgeCommitmentConflict *cac) 486 { 487 TALER_denom_sig_free (&cac->prev_denom_sig); 488 cac->conflict_detail = NULL; 489 } 490 491 492 enum GNUNET_GenericReturnValue 493 TALER_EXCHANGE_check_coin_conflict_ ( 494 const struct TALER_EXCHANGE_Keys *keys, 495 enum TALER_ErrorCode ec, 496 const json_t *proof, 497 TALER_EXCHANGE_CoinLookupCallback_ lookup, 498 void *lookup_cls, 499 struct TALER_EXCHANGE_CoinConflict *cc) 500 { 501 const struct TALER_DenominationHashP *h_denom_pub = NULL; 502 const struct TALER_AgeCommitmentHashP *h_age_commitment = NULL; 503 504 cc->ec = TALER_EC_NONE; 505 switch (ec) 506 { 507 case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: 508 { 509 struct TALER_EXCHANGE_CoinDenominationConflict *cdc 510 = &cc->details.denomination_conflict; 511 512 if (GNUNET_OK != 513 TALER_EXCHANGE_parse_coin_denomination_conflict_ (proof, 514 cdc)) 515 { 516 GNUNET_break_op (0); 517 return GNUNET_SYSERR; 518 } 519 if ( (GNUNET_OK != 520 lookup (lookup_cls, 521 &cdc->coin_pub, 522 &h_denom_pub, 523 &h_age_commitment)) || 524 (GNUNET_SYSERR == 525 TALER_EXCHANGE_check_coin_denomination_conflict_ (keys, 526 h_denom_pub, 527 cdc)) ) 528 { 529 GNUNET_break_op (0); 530 TALER_EXCHANGE_free_coin_denomination_conflict_ (cdc); 531 return GNUNET_SYSERR; 532 } 533 cc->ec = ec; 534 return GNUNET_OK; 535 } 536 case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH: 537 { 538 struct TALER_EXCHANGE_CoinAgeCommitmentConflict *cac 539 = &cc->details.age_commitment_conflict; 540 541 if (GNUNET_OK != 542 TALER_EXCHANGE_parse_coin_age_commitment_conflict_ (proof, 543 cac)) 544 { 545 GNUNET_break_op (0); 546 return GNUNET_SYSERR; 547 } 548 if ( (GNUNET_OK != 549 lookup (lookup_cls, 550 &cac->coin_pub, 551 &h_denom_pub, 552 &h_age_commitment)) || 553 (GNUNET_SYSERR == 554 TALER_EXCHANGE_check_coin_age_commitment_conflict_ ( 555 keys, 556 h_denom_pub, 557 h_age_commitment, 558 cac)) ) 559 { 560 GNUNET_break_op (0); 561 TALER_EXCHANGE_free_coin_age_commitment_conflict_ (cac); 562 return GNUNET_SYSERR; 563 } 564 cc->ec = ec; 565 return GNUNET_OK; 566 } 567 default: 568 GNUNET_break (0); 569 return GNUNET_SYSERR; 570 } 571 } 572 573 574 void 575 TALER_EXCHANGE_free_coin_conflict_ ( 576 const struct TALER_EXCHANGE_HttpResponse *hr, 577 struct TALER_EXCHANGE_CoinConflict *cc) 578 { 579 if (MHD_HTTP_CONFLICT != hr->http_status) 580 return; 581 if (hr->ec != cc->ec) 582 return; 583 switch (cc->ec) 584 { 585 case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: 586 TALER_EXCHANGE_free_coin_denomination_conflict_ ( 587 &cc->details.denomination_conflict); 588 break; 589 case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH: 590 TALER_EXCHANGE_free_coin_age_commitment_conflict_ ( 591 &cc->details.age_commitment_conflict); 592 break; 593 default: 594 break; 595 } 596 cc->ec = TALER_EC_NONE; 597 } 598 599 600 enum GNUNET_GenericReturnValue 601 TALER_EXCHANGE_get_min_denomination_ ( 602 const struct TALER_EXCHANGE_Keys *keys, 603 struct TALER_Amount *min) 604 { 605 bool have_min = false; 606 for (unsigned int i = 0; i<keys->num_denom_keys; i++) 607 { 608 const struct TALER_EXCHANGE_DenomPublicKey *dk = &keys->denom_keys[i]; 609 610 if (! have_min) 611 { 612 *min = dk->value; 613 have_min = true; 614 continue; 615 } 616 if (1 != TALER_amount_cmp (min, 617 &dk->value)) 618 continue; 619 *min = dk->value; 620 } 621 if (! have_min) 622 { 623 GNUNET_break (0); 624 return GNUNET_SYSERR; 625 } 626 return GNUNET_OK; 627 } 628 629 630 enum GNUNET_GenericReturnValue 631 TALER_EXCHANGE_verify_deposit_signature_ ( 632 const struct TALER_EXCHANGE_DepositContractDetail *dcd, 633 const struct TALER_ExtensionPolicyHashP *ech, 634 const struct TALER_MerchantWireHashP *h_wire, 635 const struct TALER_EXCHANGE_CoinDepositDetail *cdd, 636 const struct TALER_EXCHANGE_DenomPublicKey *dki) 637 { 638 if (GNUNET_OK != 639 TALER_wallet_deposit_verify (&cdd->amount, 640 &dki->fees.deposit, 641 h_wire, 642 &dcd->h_contract_terms, 643 &dcd->wallet_data_hash, 644 &cdd->h_age_commitment, 645 ech, 646 &cdd->h_denom_pub, 647 dcd->wallet_timestamp, 648 &dcd->merchant_pub, 649 dcd->refund_deadline, 650 &cdd->coin_pub, 651 &cdd->coin_sig)) 652 { 653 GNUNET_break_op (0); 654 TALER_LOG_WARNING ("Invalid coin signature on /deposit request!\n"); 655 TALER_LOG_DEBUG ("... amount_with_fee was %s\n", 656 TALER_amount2s (&cdd->amount)); 657 TALER_LOG_DEBUG ("... deposit_fee was %s\n", 658 TALER_amount2s (&dki->fees.deposit)); 659 return GNUNET_SYSERR; 660 } 661 662 /* check coin signature */ 663 { 664 struct TALER_CoinPublicInfo coin_info = { 665 .coin_pub = cdd->coin_pub, 666 .denom_pub_hash = cdd->h_denom_pub, 667 .denom_sig = cdd->denom_sig, 668 .h_age_commitment = cdd->h_age_commitment, 669 }; 670 671 if (GNUNET_YES != 672 TALER_test_coin_valid (&coin_info, 673 &dki->key)) 674 { 675 GNUNET_break_op (0); 676 TALER_LOG_WARNING ("Invalid coin passed for /deposit\n"); 677 return GNUNET_SYSERR; 678 } 679 } 680 681 /* Check coin does make a contribution */ 682 if (0 < TALER_amount_cmp (&dki->fees.deposit, 683 &cdd->amount)) 684 { 685 GNUNET_break_op (0); 686 TALER_LOG_WARNING ("Deposit amount smaller than fee\n"); 687 return GNUNET_SYSERR; 688 } 689 return GNUNET_OK; 690 } 691 692 693 /** 694 * Parse account restriction in @a jrest into @a rest. 695 * 696 * @param jresta array of account restrictions in JSON 697 * @param[out] resta_len set to length of @a resta 698 * @param[out] resta account restriction array to set 699 * @return #GNUNET_OK on success 700 */ 701 static enum GNUNET_GenericReturnValue 702 parse_restrictions (const json_t *jresta, 703 unsigned int *resta_len, 704 struct TALER_EXCHANGE_AccountRestriction **resta) 705 { 706 size_t alen; 707 708 if (! json_is_array (jresta)) 709 { 710 GNUNET_break_op (0); 711 return GNUNET_SYSERR; 712 } 713 alen = json_array_size (jresta); 714 if (0 == alen) 715 { 716 /* no restrictions, perfectly OK */ 717 *resta = NULL; 718 return GNUNET_OK; 719 } 720 *resta_len = (unsigned int) alen; 721 GNUNET_assert (alen == *resta_len); 722 *resta = GNUNET_new_array (*resta_len, 723 struct TALER_EXCHANGE_AccountRestriction); 724 for (unsigned int i = 0; i< *resta_len; i++) 725 { 726 const json_t *jr = json_array_get (jresta, 727 i); 728 struct TALER_EXCHANGE_AccountRestriction *ar = &(*resta)[i]; 729 const char *type = json_string_value (json_object_get (jr, 730 "type")); 731 732 if (NULL == type) 733 { 734 GNUNET_break (0); 735 goto fail; 736 } 737 if (0 == strcmp (type, 738 "deny")) 739 { 740 ar->type = TALER_EXCHANGE_AR_DENY; 741 continue; 742 } 743 if (0 == strcmp (type, 744 "regex")) 745 { 746 const char *regex; 747 const char *hint; 748 struct GNUNET_JSON_Specification spec[] = { 749 GNUNET_JSON_spec_string ( 750 "payto_regex", 751 ®ex), 752 GNUNET_JSON_spec_string ( 753 "human_hint", 754 &hint), 755 GNUNET_JSON_spec_mark_optional ( 756 GNUNET_JSON_spec_json ( 757 "human_hint_i18n", 758 &ar->details.regex.human_hint_i18n), 759 NULL), 760 GNUNET_JSON_spec_end () 761 }; 762 763 if (GNUNET_OK != 764 GNUNET_JSON_parse (jr, 765 spec, 766 NULL, NULL)) 767 { 768 /* bogus reply */ 769 GNUNET_break_op (0); 770 goto fail; 771 } 772 ar->type = TALER_EXCHANGE_AR_REGEX; 773 ar->details.regex.posix_egrep = GNUNET_strdup (regex); 774 ar->details.regex.human_hint = GNUNET_strdup (hint); 775 continue; 776 } 777 /* unsupported type */ 778 GNUNET_break (0); 779 goto fail; 780 } 781 return GNUNET_OK; 782 fail: 783 for (unsigned int i = 0; i<*resta_len; i++) 784 { 785 struct TALER_EXCHANGE_AccountRestriction *ar = &(*resta)[i]; 786 787 if (TALER_EXCHANGE_AR_REGEX == ar->type) 788 { 789 GNUNET_free (ar->details.regex.posix_egrep); 790 GNUNET_free (ar->details.regex.human_hint); 791 json_decref (ar->details.regex.human_hint_i18n); 792 } 793 } 794 GNUNET_free (*resta); 795 *resta_len = 0; 796 return GNUNET_SYSERR; 797 } 798 799 800 enum GNUNET_GenericReturnValue 801 TALER_EXCHANGE_parse_accounts ( 802 const struct TALER_MasterPublicKeyP *master_pub, 803 const json_t *accounts, 804 unsigned int was_length, 805 struct TALER_EXCHANGE_WireAccount was[static was_length]) 806 { 807 memset (was, 808 0, 809 sizeof (struct TALER_EXCHANGE_WireAccount) * was_length); 810 GNUNET_assert (was_length == 811 json_array_size (accounts)); 812 for (unsigned int i = 0; 813 i<was_length; 814 i++) 815 { 816 struct TALER_EXCHANGE_WireAccount *wa = &was[i]; 817 struct TALER_FullPayto payto_uri; 818 const char *conversion_url = NULL; 819 const char *open_banking_gateway = NULL; 820 const char *prepared_transfer_url = NULL; 821 const char *bank_label = NULL; 822 int64_t priority = 0; 823 const json_t *credit_restrictions; 824 const json_t *debit_restrictions; 825 struct GNUNET_JSON_Specification spec_account[] = { 826 TALER_JSON_spec_full_payto_uri ("payto_uri", 827 &payto_uri), 828 GNUNET_JSON_spec_mark_optional ( 829 TALER_JSON_spec_web_url ("conversion_url", 830 &conversion_url), 831 NULL), 832 GNUNET_JSON_spec_mark_optional ( 833 TALER_JSON_spec_web_url ("open_banking_gateway", 834 &open_banking_gateway), 835 NULL), 836 GNUNET_JSON_spec_mark_optional ( 837 TALER_JSON_spec_web_url ("prepared_transfer_url", 838 &prepared_transfer_url), 839 NULL), 840 GNUNET_JSON_spec_mark_optional ( 841 GNUNET_JSON_spec_int64 ("priority", 842 &priority), 843 NULL), 844 GNUNET_JSON_spec_mark_optional ( 845 GNUNET_JSON_spec_string ("bank_label", 846 &bank_label), 847 NULL), 848 GNUNET_JSON_spec_array_const ("credit_restrictions", 849 &credit_restrictions), 850 GNUNET_JSON_spec_array_const ("debit_restrictions", 851 &debit_restrictions), 852 GNUNET_JSON_spec_fixed_auto ("master_sig", 853 &wa->master_sig), 854 GNUNET_JSON_spec_end () 855 }; 856 json_t *account; 857 858 account = json_array_get (accounts, 859 i); 860 if (GNUNET_OK != 861 GNUNET_JSON_parse (account, 862 spec_account, 863 NULL, NULL)) 864 { 865 /* bogus reply */ 866 GNUNET_break_op (0); 867 return GNUNET_SYSERR; 868 } 869 if ( (NULL != master_pub) && 870 (! ( ( (NULL == open_banking_gateway) && 871 (NULL == prepared_transfer_url) && 872 (GNUNET_OK == 873 TALER_exchange_wire_signature_check32 ( 874 payto_uri, 875 conversion_url, 876 debit_restrictions, 877 credit_restrictions, 878 master_pub, 879 &wa->master_sig)) ) || 880 (GNUNET_OK == 881 TALER_exchange_wire_signature_check ( 882 payto_uri, 883 conversion_url, 884 open_banking_gateway, 885 prepared_transfer_url, 886 debit_restrictions, 887 credit_restrictions, 888 master_pub, 889 &wa->master_sig)) ) ) ) 890 { 891 /* bogus reply */ 892 GNUNET_break_op (0); 893 return GNUNET_SYSERR; 894 } 895 if ( (GNUNET_OK != 896 parse_restrictions (credit_restrictions, 897 &wa->credit_restrictions_length, 898 &wa->credit_restrictions)) || 899 (GNUNET_OK != 900 parse_restrictions (debit_restrictions, 901 &wa->debit_restrictions_length, 902 &wa->debit_restrictions)) ) 903 { 904 /* bogus reply */ 905 GNUNET_break_op (0); 906 return GNUNET_SYSERR; 907 } 908 wa->fpayto_uri.full_payto 909 = GNUNET_strdup (payto_uri.full_payto); 910 wa->priority = priority; 911 if (NULL != conversion_url) 912 wa->conversion_url = GNUNET_strdup (conversion_url); 913 if (NULL != open_banking_gateway) 914 wa->open_banking_gateway = GNUNET_strdup (open_banking_gateway); 915 if (NULL != prepared_transfer_url) 916 wa->prepared_transfer_url = GNUNET_strdup (prepared_transfer_url); 917 if (NULL != bank_label) 918 wa->bank_label = GNUNET_strdup (bank_label); 919 } /* end 'for all accounts */ 920 return GNUNET_OK; 921 } 922 923 924 /** 925 * Free array of account restrictions. 926 * 927 * @param ar_len length of @a ar 928 * @param[in] ar array to free contents of (but not @a ar itself) 929 */ 930 static void 931 free_restrictions (unsigned int ar_len, 932 struct TALER_EXCHANGE_AccountRestriction ar[static ar_len]) 933 { 934 for (unsigned int i = 0; i<ar_len; i++) 935 { 936 struct TALER_EXCHANGE_AccountRestriction *a = &ar[i]; 937 switch (a->type) 938 { 939 case TALER_EXCHANGE_AR_INVALID: 940 GNUNET_break (0); 941 break; 942 case TALER_EXCHANGE_AR_DENY: 943 break; 944 case TALER_EXCHANGE_AR_REGEX: 945 GNUNET_free (a->details.regex.posix_egrep); 946 GNUNET_free (a->details.regex.human_hint); 947 json_decref (a->details.regex.human_hint_i18n); 948 break; 949 } 950 } 951 } 952 953 954 void 955 TALER_EXCHANGE_free_accounts ( 956 unsigned int was_len, 957 struct TALER_EXCHANGE_WireAccount was[static was_len]) 958 { 959 for (unsigned int i = 0; i<was_len; i++) 960 { 961 struct TALER_EXCHANGE_WireAccount *wa = &was[i]; 962 963 GNUNET_free (wa->fpayto_uri.full_payto); 964 GNUNET_free (wa->conversion_url); 965 GNUNET_free (wa->open_banking_gateway); 966 GNUNET_free (wa->prepared_transfer_url); 967 GNUNET_free (wa->bank_label); 968 free_restrictions (wa->credit_restrictions_length, 969 wa->credit_restrictions); 970 GNUNET_array_grow (wa->credit_restrictions, 971 wa->credit_restrictions_length, 972 0); 973 free_restrictions (wa->debit_restrictions_length, 974 wa->debit_restrictions); 975 GNUNET_array_grow (wa->debit_restrictions, 976 wa->debit_restrictions_length, 977 0); 978 } 979 } 980 981 982 enum GNUNET_GenericReturnValue 983 TALER_EXCHANGE_keys_test_account_allowed ( 984 const struct TALER_EXCHANGE_Keys *keys, 985 bool check_credit, 986 const struct TALER_NormalizedPayto payto_uri) 987 { 988 /* For all accounts of the exchange */ 989 for (unsigned int i = 0; i<keys->accounts_len; i++) 990 { 991 const struct TALER_EXCHANGE_WireAccount *account 992 = &keys->accounts[i]; 993 994 /* KYC auth transfers are never supported with conversion */ 995 if (NULL != account->conversion_url) 996 continue; 997 /* filter by source account by credit_restrictions */ 998 if (GNUNET_YES != 999 TALER_EXCHANGE_test_account_allowed (account, 1000 check_credit, 1001 payto_uri)) 1002 continue; 1003 /* exchange account is allowed, add it */ 1004 return true; 1005 } 1006 return false; 1007 } 1008 1009 1010 /** 1011 * We received an #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS response code. 1012 * Parse the JSON response and initialize the @a uflr object. 1013 * 1014 * @param[out] uflr data structure to initialize 1015 * @param j JSON response to parse 1016 * @return #GNUNET_OK on success 1017 */ 1018 enum GNUNET_GenericReturnValue 1019 TALER_EXCHANGE_parse_451 (struct TALER_EXCHANGE_KycNeededRedirect *uflr, 1020 const json_t *j) 1021 { 1022 struct GNUNET_JSON_Specification spec[] = { 1023 GNUNET_JSON_spec_fixed_auto ( 1024 "h_payto", 1025 &uflr->h_payto), 1026 GNUNET_JSON_spec_uint64 ( 1027 "requirement_row", 1028 &uflr->requirement_row), 1029 GNUNET_JSON_spec_mark_optional ( 1030 GNUNET_JSON_spec_fixed_auto ( 1031 "account_pub", 1032 &uflr->account_pub), 1033 NULL), 1034 GNUNET_JSON_spec_mark_optional ( 1035 GNUNET_JSON_spec_bool ( 1036 "bad_kyc_auth", 1037 &uflr->bad_kyc_auth), 1038 NULL), 1039 GNUNET_JSON_spec_end () 1040 }; 1041 1042 if (GNUNET_OK != 1043 GNUNET_JSON_parse (j, 1044 spec, 1045 NULL, 1046 NULL)) 1047 { 1048 GNUNET_break_op (0); 1049 return GNUNET_SYSERR; 1050 } 1051 return GNUNET_OK; 1052 } 1053 1054 1055 json_t * 1056 TALER_EXCHANGE_recoup_coin_data_ ( 1057 size_t num_coins, 1058 const struct TALER_EXCHANGE_RecoupCoin coins[static num_coins], 1059 const struct TALER_BlindingMasterSeedP *blinding_seed, 1060 bool for_melt, 1061 struct TALER_CoinSpendPublicKeyP recouped_pubs[static num_coins], 1062 struct TALER_EXCHANGE_RecoupedCoinInfo_ recouped_infos[static num_coins], 1063 size_t *num_recouped) 1064 { 1065 json_t *arr = json_array (); 1066 1067 GNUNET_assert (NULL != arr); 1068 *num_recouped = 0; 1069 for (size_t i = 0; i < num_coins; i++) 1070 { 1071 const struct TALER_EXCHANGE_RecoupCoin *c = &coins[i]; 1072 struct TALER_CoinSpendPrivateKeyP coin_priv; 1073 struct TALER_CoinSpendPublicKeyP coin_pub; 1074 union GNUNET_CRYPTO_BlindingSecretP bks; 1075 union GNUNET_CRYPTO_BlindSessionNonce nonce; 1076 const union GNUNET_CRYPTO_BlindSessionNonce *np = NULL; 1077 json_t *entry; 1078 1079 const struct TALER_ExchangeBlindingValues *alg_values 1080 = c->exchange_vals; 1081 1082 if ( (NULL == c->pk) || 1083 (NULL == c->ps) ) 1084 { 1085 GNUNET_break (0); 1086 json_decref (arr); 1087 return NULL; 1088 } 1089 if (NULL == alg_values) 1090 { 1091 /* documented for RSA denominations, which have no 1092 exchange-contributed blinding values */ 1093 if (GNUNET_CRYPTO_BSA_RSA != c->pk->key.bsign_pub_key->cipher) 1094 { 1095 GNUNET_break (0); 1096 json_decref (arr); 1097 return NULL; 1098 } 1099 alg_values = TALER_denom_ewv_rsa_singleton (); 1100 } 1101 TALER_planchet_setup_coin_priv (c->ps, 1102 alg_values, 1103 &coin_priv); 1104 TALER_planchet_blinding_secret_create (c->ps, 1105 alg_values, 1106 &bks); 1107 GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv, 1108 &coin_pub.eddsa_pub); 1109 if (GNUNET_CRYPTO_BSA_CS == c->pk->key.bsign_pub_key->cipher) 1110 { 1111 if (NULL == blinding_seed) 1112 { 1113 GNUNET_break (0); 1114 json_decref (arr); 1115 return NULL; 1116 } 1117 TALER_cs_nonce_derive_indexed (blinding_seed, 1118 for_melt, 1119 (uint32_t) i, 1120 &nonce.cs_nonce); 1121 np = &nonce; 1122 } 1123 if (NULL == c->denom_sig) 1124 { 1125 struct TALER_PlanchetDetail pd; 1126 struct TALER_CoinPubHashP c_hash; 1127 struct TALER_BlindedCoinHashP bch; 1128 1129 if (GNUNET_OK != 1130 TALER_planchet_prepare (&c->pk->key, 1131 alg_values, 1132 &bks, 1133 np, 1134 &coin_priv, 1135 c->h_age_commitment, 1136 &c_hash, 1137 &pd)) 1138 { 1139 GNUNET_break (0); 1140 json_decref (arr); 1141 return NULL; 1142 } 1143 TALER_coin_ev_hash (&pd.blinded_planchet, 1144 &c->pk->h_key, 1145 &bch); 1146 TALER_blinded_planchet_free (&pd.blinded_planchet); 1147 entry = GNUNET_JSON_PACK ( 1148 GNUNET_JSON_pack_string ("type", 1149 "hash"), 1150 GNUNET_JSON_pack_data_auto ("h_coin_ev", 1151 &bch)); 1152 } 1153 else 1154 { 1155 struct TALER_CoinSpendSignatureP coin_sig; 1156 1157 if (for_melt) 1158 TALER_wallet_recoup_refresh_sign (&c->pk->h_key, 1159 &bks, 1160 &coin_priv, 1161 &coin_sig); 1162 else 1163 TALER_wallet_recoup_sign (&c->pk->h_key, 1164 &bks, 1165 &coin_priv, 1166 &coin_sig); 1167 entry = GNUNET_JSON_PACK ( 1168 GNUNET_JSON_pack_string ("type", 1169 "recoup"), 1170 GNUNET_JSON_pack_data_auto ("coin_pub", 1171 &coin_pub), 1172 GNUNET_JSON_pack_data_auto ("denom_pub_h", 1173 &c->pk->h_key), 1174 TALER_JSON_pack_denom_sig ("denom_sig", 1175 c->denom_sig), 1176 GNUNET_JSON_pack_data_auto ("coin_blinding_secret", 1177 &bks), 1178 GNUNET_JSON_pack_allow_null ( 1179 GNUNET_JSON_pack_data_varsize ("h_age_commitment", 1180 c->h_age_commitment, 1181 (NULL == c->h_age_commitment) 1182 ? 0 1183 : sizeof (*c->h_age_commitment))), 1184 GNUNET_JSON_pack_data_auto ("coin_sig", 1185 &coin_sig)); 1186 recouped_pubs[*num_recouped] = coin_pub; 1187 recouped_infos[*num_recouped].h_denom_pub = c->pk->h_key; 1188 recouped_infos[*num_recouped].have_age = (NULL != c->h_age_commitment); 1189 if (NULL != c->h_age_commitment) 1190 recouped_infos[*num_recouped].h_age_commitment = *c->h_age_commitment; 1191 (*num_recouped)++; 1192 } 1193 GNUNET_assert (0 == 1194 json_array_append_new (arr, 1195 entry)); 1196 } 1197 if (0 == *num_recouped) 1198 { 1199 GNUNET_break (0); 1200 json_decref (arr); 1201 return NULL; 1202 } 1203 return arr; 1204 } 1205 1206 1207 enum GNUNET_GenericReturnValue 1208 TALER_EXCHANGE_parse_recoups_ ( 1209 const json_t *j_recoups, 1210 size_t num_expected, 1211 const struct TALER_CoinSpendPublicKeyP expected_pubs[static num_expected], 1212 const struct TALER_Amount *total_amount, 1213 struct TALER_RecoupedCoin **recoups, 1214 struct GNUNET_HashCode *h_recoups) 1215 { 1216 struct TALER_RecoupedCoin *rcs; 1217 struct TALER_Amount sum; 1218 size_t idx; 1219 json_t *entry; 1220 1221 *recoups = NULL; 1222 if ( (! json_is_array (j_recoups)) || 1223 (num_expected != json_array_size (j_recoups)) ) 1224 { 1225 GNUNET_break_op (0); 1226 return GNUNET_SYSERR; 1227 } 1228 rcs = GNUNET_new_array (num_expected, 1229 struct TALER_RecoupedCoin); 1230 GNUNET_assert (GNUNET_OK == 1231 TALER_amount_set_zero (total_amount->currency, 1232 &sum)); 1233 json_array_foreach (j_recoups, idx, entry) 1234 { 1235 struct GNUNET_JSON_Specification spec[] = { 1236 GNUNET_JSON_spec_fixed_auto ("coin_pub", 1237 &rcs[idx].coin_pub), 1238 TALER_JSON_spec_amount_any ("amount", 1239 &rcs[idx].amount), 1240 GNUNET_JSON_spec_end () 1241 }; 1242 1243 if (GNUNET_OK != 1244 GNUNET_JSON_parse (entry, 1245 spec, 1246 NULL, NULL)) 1247 { 1248 GNUNET_break_op (0); 1249 GNUNET_free (rcs); 1250 return GNUNET_SYSERR; 1251 } 1252 if (0 != 1253 GNUNET_memcmp (&rcs[idx].coin_pub, 1254 &expected_pubs[idx])) 1255 { 1256 GNUNET_break_op (0); 1257 GNUNET_free (rcs); 1258 return GNUNET_SYSERR; 1259 } 1260 if ( (GNUNET_OK != 1261 TALER_amount_cmp_currency (&sum, 1262 &rcs[idx].amount)) || 1263 (0 > 1264 TALER_amount_add (&sum, 1265 &sum, 1266 &rcs[idx].amount)) ) 1267 { 1268 GNUNET_break_op (0); 1269 GNUNET_free (rcs); 1270 return GNUNET_SYSERR; 1271 } 1272 } 1273 if ( (GNUNET_OK != 1274 TALER_amount_cmp_currency (&sum, 1275 total_amount)) || 1276 (0 != 1277 TALER_amount_cmp (&sum, 1278 total_amount)) ) 1279 { 1280 GNUNET_break_op (0); 1281 GNUNET_free (rcs); 1282 return GNUNET_SYSERR; 1283 } 1284 TALER_recoup_batch_hash (num_expected, 1285 rcs, 1286 h_recoups); 1287 *recoups = rcs; 1288 return GNUNET_OK; 1289 } 1290 1291 1292 /* end of exchange_api_common.c */