exchange_api_withdraw.c (55214B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2023-2025 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU 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_withdraw.c 19 * @brief Implementation of /withdraw requests 20 * @author Özgür Kesim 21 */ 22 /** 23 * We want the "dangerous" exports here as these are OUR exports 24 * and we want to check that the prototypes match. 25 */ 26 #define TALER_TESTING_EXPORTS_DANGEROUS 1 27 #include "taler/platform.h" 28 #include <gnunet/gnunet_common.h> 29 #include <jansson.h> 30 #include <microhttpd.h> /* just for HTTP status codes */ 31 #include <gnunet/gnunet_util_lib.h> 32 #include <gnunet/gnunet_json_lib.h> 33 #include <gnunet/gnunet_curl_lib.h> 34 #include <sys/wait.h> 35 #include "taler/taler_curl_lib.h" 36 #include "taler/taler_error_codes.h" 37 #include "taler/taler_json_lib.h" 38 #include "taler/taler_exchange_service.h" 39 #include "exchange_api_common.h" 40 #include "exchange_api_handle.h" 41 #include "taler/taler_signatures.h" 42 #include "exchange_api_curl_defaults.h" 43 #include "taler/taler_util.h" 44 45 /** 46 * A CoinCandidate is populated from a master secret. 47 * The data is copied from and generated out of the client's input. 48 */ 49 struct CoinCandidate 50 { 51 /** 52 * The details derived form the master secrets 53 */ 54 struct TALER_EXCHANGE_WithdrawCoinPrivateDetails details; 55 56 /** 57 * Blinded hash of the coin 58 **/ 59 struct TALER_BlindedCoinHashP blinded_coin_h; 60 61 }; 62 63 64 /** 65 * Closure for a call to /blinding-prepare, contains data that is needed to process 66 * the result. 67 */ 68 struct BlindingPrepareClosure 69 { 70 /** 71 * Number of coins in the blinding-prepare step. 72 * Not that this number might be smaller than the total number 73 * of coins in the withdraw, as the prepare is only necessary 74 * for CS denominations 75 */ 76 size_t num_prepare_coins; 77 78 /** 79 * Array of @e num_prepare_coins of data per coin 80 */ 81 struct BlindingPrepareCoinData 82 { 83 /** 84 * Pointer to the candidate in CoinData.candidates, 85 * to continue to build its contents based on the results from /blinding-prepare 86 */ 87 struct CoinCandidate *candidate; 88 89 /** 90 * Planchet to finally generate in the corresponding candidate 91 * in CoindData.planchet_details 92 */ 93 struct TALER_PlanchetDetail *planchet; 94 95 /** 96 * Denomination information, needed for the 97 * step after /blinding-prepare 98 */ 99 const struct TALER_DenominationPublicKey *denom_pub; 100 101 /** 102 * True, if denomination supports age restriction 103 */ 104 bool age_denom; 105 106 /** 107 * The index into the array of returned values from the call to 108 * /blinding-prepare that are to be used for this coin. 109 */ 110 size_t cs_idx; 111 112 } *coins; 113 114 /** 115 * Number of seeds requested. This may differ from @e num_prepare_coins 116 * in case of a withdraw with required age proof, in which case 117 * @e num_prepare_coins = TALER_CNC_KAPPA * @e num_seeds 118 */ 119 size_t num_nonces; 120 121 /** 122 * Array of @e num_nonces calculated nonces. 123 */ 124 union GNUNET_CRYPTO_BlindSessionNonce *nonces; 125 126 /** 127 * Handler to the originating call to /withdraw, needed to either 128 * cancel the running withdraw request (on failure of the current call 129 * to /blinding-prepare), or to eventually perform the protocol, once all 130 * blinding-prepare requests have successfully finished. 131 */ 132 struct TALER_EXCHANGE_WithdrawHandle *withdraw_handle; 133 134 }; 135 136 137 /** 138 * Data we keep per coin in the batch. 139 * This is copied from and generated out of the input provided 140 * by the client. 141 */ 142 struct CoinData 143 { 144 /** 145 * The denomination of the coin. 146 */ 147 struct TALER_EXCHANGE_DenomPublicKey denom_pub; 148 149 /** 150 * The Candidates for the coin. If the batch is not age-restricted, 151 * only index 0 is used. 152 */ 153 struct CoinCandidate candidates[TALER_CNC_KAPPA]; 154 155 /** 156 * Details of the planchet(s). If the batch is not age-restricted, 157 * only index 0 is used. 158 */ 159 struct TALER_PlanchetDetail planchet_details[TALER_CNC_KAPPA]; 160 }; 161 162 163 /** 164 * A /withdraw request-handle for calls with pre-blinded planchets. 165 * Returned by TALER_EXCHANGE_withdraw_blinded. 166 */ 167 struct TALER_EXCHANGE_WithdrawBlindedHandle 168 { 169 170 /** 171 * Reserve private key. 172 */ 173 const struct TALER_ReservePrivateKeyP *reserve_priv; 174 175 /** 176 * Reserve public key, calculated 177 */ 178 struct TALER_ReservePublicKeyP reserve_pub; 179 180 /** 181 * Signature of the reserve for the request, calculated after all 182 * parameters for the coins are collected. 183 */ 184 struct TALER_ReserveSignatureP reserve_sig; 185 186 /* 187 * The denomination keys of the exchange 188 */ 189 struct TALER_EXCHANGE_Keys *keys; 190 191 /** 192 * The hash of all the planchets 193 */ 194 struct TALER_HashBlindedPlanchetsP planchets_h; 195 196 /** 197 * Seed used for the derival of blinding factors for denominations 198 * with Clause-Schnorr cipher. We derive this from the master seed 199 * for the withdraw, but independent from the other planchet seeds. 200 */ 201 const struct TALER_BlindingMasterSeedP *blinding_seed; 202 203 /** 204 * Total amount requested (without fee). 205 */ 206 struct TALER_Amount amount; 207 208 /** 209 * Total withdraw fee 210 */ 211 struct TALER_Amount fee; 212 213 /** 214 * Is this call for age-restriced coins, with age proof? 215 */ 216 bool with_age_proof; 217 218 /** 219 * If @e with_age_proof is true or @max_age is > 0, 220 * the age mask to use, extracted from the denominations. 221 * MUST be the same for all denominations. 222 */ 223 struct TALER_AgeMask age_mask; 224 225 /** 226 * The maximum age to commit to. If @e with_age_proof 227 * is true, the client will need to proof the correct setting 228 * of age-restriction on the coins via an additional call 229 * to /reveal-withdraw. 230 */ 231 uint8_t max_age; 232 233 /** 234 * If @e with_age_proof is true, the hash of all the selected planchets 235 */ 236 struct TALER_HashBlindedPlanchetsP selected_h; 237 238 /** 239 * Length of the either the @e blinded.input or 240 * the @e blinded.with_age_proof_input array, 241 * depending on @e with_age_proof. 242 */ 243 size_t num_input; 244 245 union 246 { 247 /** 248 * The blinded planchet input candidates for age-restricted coins 249 * for the call to /withdraw 250 */ 251 const struct 252 TALER_EXCHANGE_WithdrawBlindedAgeRestrictedCoinInput *with_age_proof_input; 253 254 /** 255 * The blinded planchet input for the call to /withdraw via 256 * TALER_EXCHANGE_withdraw_blinded, for age-unrestricted coins. 257 */ 258 const struct TALER_EXCHANGE_WithdrawBlindedCoinInput *input; 259 260 } blinded; 261 262 /** 263 * The url for this request. 264 */ 265 char *request_url; 266 267 /** 268 * Context for curl. 269 */ 270 struct GNUNET_CURL_Context *curl_ctx; 271 272 /** 273 * CURL handle for the request job. 274 */ 275 struct GNUNET_CURL_Job *job; 276 277 /** 278 * Post Context 279 */ 280 struct TALER_CURL_PostContext post_ctx; 281 282 /** 283 * Function to call with withdraw response results. 284 */ 285 TALER_EXCHANGE_WithdrawBlindedCallback callback; 286 287 /** 288 * Closure for @e blinded_callback 289 */ 290 void *callback_cls; 291 }; 292 293 /** 294 * A /withdraw request-handle for calls from 295 * a wallet, i. e. when blinding data is available. 296 */ 297 struct TALER_EXCHANGE_WithdrawHandle 298 { 299 300 /** 301 * The base-URL of the exchange. 302 */ 303 const char *exchange_url; 304 305 /** 306 * Seed to derive of all seeds for the coins. 307 */ 308 struct TALER_WithdrawMasterSeedP seed; 309 310 /** 311 * If @e with_age_proof is true, the derived TALER_CNC_KAPPA many 312 * seeds for candidate batches. 313 */ 314 struct TALER_KappaWithdrawMasterSeedP kappa_seed; 315 316 /** 317 * True if @e blinding_seed is filled, that is, if 318 * any of the denominations is of cipher type CS 319 */ 320 bool has_blinding_seed; 321 322 /** 323 * Seed used for the derivation of blinding factors for denominations 324 * with Clause-Schnorr cipher. We derive this from the master seed 325 * for the withdraw, but independent from the other planchet seeds. 326 * Only valid when @e has_blinding_seed is true; 327 */ 328 struct TALER_BlindingMasterSeedP blinding_seed; 329 330 /** 331 * Reserve private key. 332 */ 333 const struct TALER_ReservePrivateKeyP *reserve_priv; 334 335 /** 336 * Reserve public key, calculated 337 */ 338 struct TALER_ReservePublicKeyP reserve_pub; 339 340 /** 341 * Signature of the reserve for the request, calculated after all 342 * parameters for the coins are collected. 343 */ 344 struct TALER_ReserveSignatureP reserve_sig; 345 346 /* 347 * The denomination keys of the exchange 348 */ 349 struct TALER_EXCHANGE_Keys *keys; 350 351 /** 352 * True, if the withdraw is for age-restricted coins, with age-proof. 353 * The denominations MUST support age restriction. 354 */ 355 bool with_age_proof; 356 357 /** 358 * If @e with_age_proof is true, the age mask, extracted 359 * from the denominations. 360 * MUST be the same for all denominations. 361 * 362 */ 363 struct TALER_AgeMask age_mask; 364 365 /** 366 * The maximum age to commit to. If @e with_age_proof 367 * is true, the client will need to proof the correct setting 368 * of age-restriction on the coins via an additional call 369 * to /reveal-withdraw. 370 */ 371 uint8_t max_age; 372 373 /** 374 * Length of the @e coin_data Array 375 */ 376 size_t num_coins; 377 378 /** 379 * Array of per-coin data 380 */ 381 struct CoinData *coin_data; 382 383 /** 384 * Context for curl. 385 */ 386 struct GNUNET_CURL_Context *curl_ctx; 387 388 /** 389 * Function to call with withdraw response results. 390 */ 391 TALER_EXCHANGE_WithdrawCallback callback; 392 393 /** 394 * Closure for @e callback 395 */ 396 void *callback_cls; 397 398 /* The handler for the call to /blinding-prepare, needed for CS denominations */ 399 struct TALER_EXCHANGE_BlindingPrepareHandle *blinding_prepare_handle; 400 401 /* The Handler for the actual call to the exchange */ 402 struct TALER_EXCHANGE_WithdrawBlindedHandle *withdraw_blinded_handle; 403 }; 404 405 406 /** 407 * We got a 200 OK response for the /withdraw operation. 408 * Extract the signatures and return them to the caller. 409 * 410 * @param wbh operation handle 411 * @param j_response reply from the exchange 412 * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors 413 */ 414 static enum GNUNET_GenericReturnValue 415 withdraw_blinded_ok ( 416 struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh, 417 const json_t *j_response) 418 { 419 struct TALER_EXCHANGE_WithdrawBlindedResponse response = { 420 .hr.reply = j_response, 421 .hr.http_status = MHD_HTTP_OK, 422 }; 423 const json_t *j_sigs; 424 struct GNUNET_JSON_Specification spec[] = { 425 GNUNET_JSON_spec_array_const ("ev_sigs", 426 &j_sigs), 427 GNUNET_JSON_spec_end () 428 }; 429 430 if (GNUNET_OK != 431 GNUNET_JSON_parse (j_response, 432 spec, 433 NULL, NULL)) 434 { 435 GNUNET_break_op (0); 436 return GNUNET_SYSERR; 437 } 438 439 if (wbh->num_input != json_array_size (j_sigs)) 440 { 441 /* Number of coins generated does not match our expectation */ 442 GNUNET_break_op (0); 443 return GNUNET_SYSERR; 444 } 445 446 { 447 struct TALER_BlindedDenominationSignature denoms_sig[wbh->num_input]; 448 449 memset (denoms_sig, 450 0, 451 sizeof(denoms_sig)); 452 453 /* Reconstruct the coins and unblind the signatures */ 454 { 455 json_t *j_sig; 456 size_t i; 457 458 json_array_foreach (j_sigs, i, j_sig) 459 { 460 struct GNUNET_JSON_Specification ispec[] = { 461 TALER_JSON_spec_blinded_denom_sig (NULL, 462 &denoms_sig[i]), 463 GNUNET_JSON_spec_end () 464 }; 465 466 if (GNUNET_OK != 467 GNUNET_JSON_parse (j_sig, 468 ispec, 469 NULL, NULL)) 470 { 471 GNUNET_break_op (0); 472 return GNUNET_SYSERR; 473 } 474 } 475 } 476 477 response.details.ok.num_sigs = wbh->num_input; 478 response.details.ok.blinded_denom_sigs = denoms_sig; 479 response.details.ok.planchets_h = wbh->planchets_h; 480 wbh->callback ( 481 wbh->callback_cls, 482 &response); 483 /* Make sure the callback isn't called again */ 484 wbh->callback = NULL; 485 /* Free resources */ 486 for (size_t i = 0; i < wbh->num_input; i++) 487 TALER_blinded_denom_sig_free (&denoms_sig[i]); 488 } 489 490 return GNUNET_OK; 491 } 492 493 494 /** 495 * We got a 201 CREATED response for the /withdraw operation. 496 * Extract the noreveal_index and return it to the caller. 497 * 498 * @param wbh operation handle 499 * @param j_response reply from the exchange 500 * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors 501 */ 502 static enum GNUNET_GenericReturnValue 503 withdraw_blinded_created ( 504 struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh, 505 const json_t *j_response) 506 { 507 struct TALER_EXCHANGE_WithdrawBlindedResponse response = { 508 .hr.reply = j_response, 509 .hr.http_status = MHD_HTTP_CREATED, 510 .details.created.planchets_h = wbh->planchets_h, 511 .details.created.num_coins = wbh->num_input, 512 }; 513 struct TALER_ExchangeSignatureP exchange_sig; 514 struct GNUNET_JSON_Specification spec[] = { 515 GNUNET_JSON_spec_uint8 ("noreveal_index", 516 &response.details.created.noreveal_index), 517 GNUNET_JSON_spec_fixed_auto ("exchange_sig", 518 &exchange_sig), 519 GNUNET_JSON_spec_fixed_auto ("exchange_pub", 520 &response.details.created.exchange_pub), 521 GNUNET_JSON_spec_end () 522 }; 523 524 if (GNUNET_OK!= 525 GNUNET_JSON_parse (j_response, 526 spec, 527 NULL, NULL)) 528 { 529 GNUNET_break_op (0); 530 return GNUNET_SYSERR; 531 } 532 533 if (GNUNET_OK != 534 TALER_exchange_online_withdraw_age_confirmation_verify ( 535 &wbh->planchets_h, 536 response.details.created.noreveal_index, 537 &response.details.created.exchange_pub, 538 &exchange_sig)) 539 { 540 GNUNET_break_op (0); 541 return GNUNET_SYSERR; 542 543 } 544 545 wbh->callback (wbh->callback_cls, 546 &response); 547 /* make sure the callback isn't called again */ 548 wbh->callback = NULL; 549 550 return GNUNET_OK; 551 } 552 553 554 /** 555 * Function called when we're done processing the 556 * HTTP /withdraw request. 557 * 558 * @param cls the `struct TALER_EXCHANGE_WithdrawBlindedHandle` 559 * @param response_code The HTTP response code 560 * @param response response data 561 */ 562 static void 563 handle_withdraw_blinded_finished ( 564 void *cls, 565 long response_code, 566 const void *response) 567 { 568 struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh = cls; 569 const json_t *j_response = response; 570 struct TALER_EXCHANGE_WithdrawBlindedResponse wbr = { 571 .hr.reply = j_response, 572 .hr.http_status = (unsigned int) response_code 573 }; 574 575 wbh->job = NULL; 576 switch (response_code) 577 { 578 case 0: 579 wbr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 580 break; 581 case MHD_HTTP_OK: 582 { 583 if (GNUNET_OK != 584 withdraw_blinded_ok ( 585 wbh, 586 j_response)) 587 { 588 GNUNET_break_op (0); 589 wbr.hr.http_status = 0; 590 wbr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 591 break; 592 } 593 GNUNET_assert (NULL == wbh->callback); 594 TALER_EXCHANGE_withdraw_blinded_cancel (wbh); 595 return; 596 } 597 case MHD_HTTP_CREATED: 598 if (GNUNET_OK != 599 withdraw_blinded_created ( 600 wbh, 601 j_response)) 602 { 603 GNUNET_break_op (0); 604 wbr.hr.http_status = 0; 605 wbr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 606 break; 607 } 608 GNUNET_assert (NULL == wbh->callback); 609 TALER_EXCHANGE_withdraw_blinded_cancel (wbh); 610 return; 611 case MHD_HTTP_BAD_REQUEST: 612 /* This should never happen, either us or the exchange is buggy 613 (or API version conflict); just pass JSON reply to the application */ 614 wbr.hr.ec = TALER_JSON_get_error_code (j_response); 615 wbr.hr.hint = TALER_JSON_get_error_hint (j_response); 616 break; 617 case MHD_HTTP_FORBIDDEN: 618 GNUNET_break_op (0); 619 /* Nothing really to verify, exchange says one of the signatures is 620 invalid; as we checked them, this should never happen, we 621 should pass the JSON reply to the application */ 622 wbr.hr.ec = TALER_JSON_get_error_code (j_response); 623 wbr.hr.hint = TALER_JSON_get_error_hint (j_response); 624 break; 625 case MHD_HTTP_NOT_FOUND: 626 /* Nothing really to verify, the exchange basically just says 627 that it doesn't know this reserve. Can happen if we 628 query before the wire transfer went through. 629 We should simply pass the JSON reply to the application. */ 630 wbr.hr.ec = TALER_JSON_get_error_code (j_response); 631 wbr.hr.hint = TALER_JSON_get_error_hint (j_response); 632 break; 633 case MHD_HTTP_CONFLICT: 634 /* The age requirements might not have been met */ 635 wbr.hr.ec = TALER_JSON_get_error_code (j_response); 636 wbr.hr.hint = TALER_JSON_get_error_hint (j_response); 637 break; 638 case MHD_HTTP_GONE: 639 /* could happen if denomination was revoked */ 640 /* Note: one might want to check /keys for revocation 641 signature here, alas tricky in case our /keys 642 is outdated => left to clients */ 643 wbr.hr.ec = TALER_JSON_get_error_code (j_response); 644 wbr.hr.hint = TALER_JSON_get_error_hint (j_response); 645 break; 646 case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 647 /* only validate reply is well-formed */ 648 { 649 struct GNUNET_JSON_Specification spec[] = { 650 GNUNET_JSON_spec_fixed_auto ( 651 "h_payto", 652 &wbr.details.unavailable_for_legal_reasons.h_payto), 653 GNUNET_JSON_spec_uint64 ( 654 "requirement_row", 655 &wbr.details.unavailable_for_legal_reasons.requirement_row), 656 GNUNET_JSON_spec_end () 657 }; 658 659 if (GNUNET_OK != 660 GNUNET_JSON_parse (j_response, 661 spec, 662 NULL, NULL)) 663 { 664 GNUNET_break_op (0); 665 wbr.hr.http_status = 0; 666 wbr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 667 break; 668 } 669 break; 670 } 671 case MHD_HTTP_INTERNAL_SERVER_ERROR: 672 /* Server had an internal issue; we should retry, but this API 673 leaves this to the application */ 674 wbr.hr.ec = TALER_JSON_get_error_code (j_response); 675 wbr.hr.hint = TALER_JSON_get_error_hint (j_response); 676 break; 677 default: 678 /* unexpected response code */ 679 GNUNET_break_op (0); 680 wbr.hr.ec = TALER_JSON_get_error_code (j_response); 681 wbr.hr.hint = TALER_JSON_get_error_hint (j_response); 682 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 683 "Unexpected response code %u/%d for exchange withdraw\n", 684 (unsigned int) response_code, 685 (int) wbr.hr.ec); 686 break; 687 } 688 wbh->callback (wbh->callback_cls, 689 &wbr); 690 TALER_EXCHANGE_withdraw_blinded_cancel (wbh); 691 } 692 693 694 /** 695 * Runs the actual withdraw operation with the blinded planchets. 696 * 697 * @param[in,out] wbh withdraw blinded handle 698 */ 699 static void 700 perform_withdraw_protocol ( 701 struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh) 702 { 703 #define FAIL_IF(cond) \ 704 do { \ 705 if ((cond)) \ 706 { \ 707 GNUNET_break (! (cond)); \ 708 goto ERROR; \ 709 } \ 710 } while (0) 711 712 json_t *j_denoms = NULL; 713 json_t *j_planchets = NULL; 714 json_t *j_request_body = NULL; 715 CURL *curlh = NULL; 716 struct GNUNET_HashContext *coins_hctx = NULL; 717 struct TALER_BlindedCoinHashP bch; 718 719 GNUNET_assert (0 < wbh->num_input); 720 721 FAIL_IF (GNUNET_OK != 722 TALER_amount_set_zero (wbh->keys->currency, 723 &wbh->amount)); 724 FAIL_IF (GNUNET_OK != 725 TALER_amount_set_zero (wbh->keys->currency, 726 &wbh->fee)); 727 728 /* Accumulate total value with fees */ 729 for (size_t i = 0; i < wbh->num_input; i++) 730 { 731 const struct TALER_EXCHANGE_DenomPublicKey *dpub = 732 wbh->with_age_proof ? 733 wbh->blinded.with_age_proof_input[i].denom_pub : 734 wbh->blinded.input[i].denom_pub; 735 736 FAIL_IF (0 > 737 TALER_amount_add (&wbh->amount, 738 &wbh->amount, 739 &dpub->value)); 740 FAIL_IF (0 > 741 TALER_amount_add (&wbh->fee, 742 &wbh->fee, 743 &dpub->fees.withdraw)); 744 745 if (GNUNET_CRYPTO_BSA_CS == 746 dpub->key.bsign_pub_key->cipher) 747 GNUNET_assert (NULL != wbh->blinding_seed); 748 749 } 750 751 if (wbh->with_age_proof || wbh->max_age > 0) 752 { 753 wbh->age_mask = 754 wbh->blinded.with_age_proof_input[0].denom_pub->key.age_mask; 755 756 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 757 "Attempting to withdraw from reserve %s with maximum age %d to proof\n", 758 TALER_B2S (&wbh->reserve_pub), 759 wbh->max_age); 760 } 761 else 762 { 763 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 764 "Attempting to withdraw from reserve %s\n", 765 TALER_B2S (&wbh->reserve_pub)); 766 } 767 768 coins_hctx = GNUNET_CRYPTO_hash_context_start (); 769 FAIL_IF (NULL == coins_hctx); 770 771 j_denoms = json_array (); 772 j_planchets = json_array (); 773 FAIL_IF ((NULL == j_denoms) || 774 (NULL == j_planchets)); 775 776 for (size_t i = 0; i< wbh->num_input; i++) 777 { 778 /* Build the denomination array */ 779 const struct TALER_EXCHANGE_DenomPublicKey *denom_pub = 780 wbh->with_age_proof ? 781 wbh->blinded.with_age_proof_input[i].denom_pub : 782 wbh->blinded.input[i].denom_pub; 783 const struct TALER_DenominationHashP *denom_h = &denom_pub->h_key; 784 json_t *jdenom; 785 786 /* The mask must be the same for all coins */ 787 FAIL_IF (wbh->with_age_proof && 788 (wbh->age_mask.bits != denom_pub->key.age_mask.bits)); 789 790 jdenom = GNUNET_JSON_from_data_auto (denom_h); 791 FAIL_IF (NULL == jdenom); 792 FAIL_IF (0 > json_array_append_new (j_denoms, 793 jdenom)); 794 } 795 796 797 /* Build the planchet array and calculate the hash over all planchets. */ 798 if (! wbh->with_age_proof) 799 { 800 for (size_t i = 0; i< wbh->num_input; i++) 801 { 802 const struct TALER_PlanchetDetail *planchet = 803 &wbh->blinded.input[i].planchet_details; 804 json_t *jc = GNUNET_JSON_PACK ( 805 TALER_JSON_pack_blinded_planchet ( 806 NULL, 807 &planchet->blinded_planchet)); 808 FAIL_IF (NULL == jc); 809 FAIL_IF (0 > json_array_append_new (j_planchets, 810 jc)); 811 812 TALER_coin_ev_hash (&planchet->blinded_planchet, 813 &planchet->denom_pub_hash, 814 &bch); 815 816 GNUNET_CRYPTO_hash_context_read (coins_hctx, 817 &bch, 818 sizeof(bch)); 819 } 820 } 821 else 822 { /* Age restricted case with required age-proof. */ 823 824 /** 825 * We collect the run of all coin candidates for the same γ index 826 * first, then γ+1 etc. 827 */ 828 for (size_t k = 0; k < TALER_CNC_KAPPA; k++) 829 { 830 struct GNUNET_HashContext *batch_ctx; 831 struct TALER_BlindedCoinHashP batch_h; 832 833 batch_ctx = GNUNET_CRYPTO_hash_context_start (); 834 FAIL_IF (NULL == batch_ctx); 835 836 for (size_t i = 0; i< wbh->num_input; i++) 837 { 838 const struct TALER_PlanchetDetail *planchet = 839 &wbh->blinded.with_age_proof_input[i].planchet_details[k]; 840 json_t *jc = GNUNET_JSON_PACK ( 841 TALER_JSON_pack_blinded_planchet ( 842 NULL, 843 &planchet->blinded_planchet)); 844 845 FAIL_IF (NULL == jc); 846 FAIL_IF (0 > json_array_append_new ( 847 j_planchets, 848 jc)); 849 850 TALER_coin_ev_hash ( 851 &planchet->blinded_planchet, 852 &planchet->denom_pub_hash, 853 &bch); 854 855 GNUNET_CRYPTO_hash_context_read ( 856 batch_ctx, 857 &bch, 858 sizeof(bch)); 859 } 860 861 GNUNET_CRYPTO_hash_context_finish ( 862 batch_ctx, 863 &batch_h.hash); 864 GNUNET_CRYPTO_hash_context_read ( 865 coins_hctx, 866 &batch_h, 867 sizeof(batch_h)); 868 } 869 } 870 871 /* Build the hash of the planchets */ 872 GNUNET_CRYPTO_hash_context_finish ( 873 coins_hctx, 874 &wbh->planchets_h.hash); 875 coins_hctx = NULL; 876 877 /* Sign the request */ 878 TALER_wallet_withdraw_sign ( 879 &wbh->amount, 880 &wbh->fee, 881 &wbh->planchets_h, 882 wbh->blinding_seed, 883 wbh->with_age_proof ? &wbh->age_mask: NULL, 884 wbh->with_age_proof ? wbh->max_age : 0, 885 wbh->reserve_priv, 886 &wbh->reserve_sig); 887 888 /* Initiate the POST-request */ 889 j_request_body = GNUNET_JSON_PACK ( 890 GNUNET_JSON_pack_string ("cipher", 891 "ED25519"), 892 GNUNET_JSON_pack_data_auto ("reserve_pub", 893 &wbh->reserve_pub), 894 GNUNET_JSON_pack_array_steal ("denoms_h", 895 j_denoms), 896 GNUNET_JSON_pack_array_steal ("coin_evs", 897 j_planchets), 898 GNUNET_JSON_pack_allow_null ( 899 wbh->with_age_proof 900 ? GNUNET_JSON_pack_int64 ("max_age", 901 wbh->max_age) 902 : GNUNET_JSON_pack_string ("max_age", 903 NULL) ), 904 GNUNET_JSON_pack_data_auto ("reserve_sig", 905 &wbh->reserve_sig)); 906 FAIL_IF (NULL == j_request_body); 907 908 if (NULL != wbh->blinding_seed) 909 { 910 json_t *j_seed = GNUNET_JSON_PACK ( 911 GNUNET_JSON_pack_data_auto ("blinding_seed", 912 wbh->blinding_seed)); 913 GNUNET_assert (NULL != j_seed); 914 GNUNET_assert (0 == 915 json_object_update_new ( 916 j_request_body, 917 j_seed)); 918 } 919 920 curlh = TALER_EXCHANGE_curl_easy_get_ (wbh->request_url); 921 FAIL_IF (NULL == curlh); 922 FAIL_IF (GNUNET_OK != 923 TALER_curl_easy_post ( 924 &wbh->post_ctx, 925 curlh, 926 j_request_body)); 927 json_decref (j_request_body); 928 j_request_body = NULL; 929 930 wbh->job = GNUNET_CURL_job_add2 ( 931 wbh->curl_ctx, 932 curlh, 933 wbh->post_ctx.headers, 934 &handle_withdraw_blinded_finished, 935 wbh); 936 FAIL_IF (NULL == wbh->job); 937 938 /* No errors, return */ 939 return; 940 941 ERROR: 942 if (NULL != coins_hctx) 943 GNUNET_CRYPTO_hash_context_abort (coins_hctx); 944 if (NULL != j_denoms) 945 json_decref (j_denoms); 946 if (NULL != j_planchets) 947 json_decref (j_planchets); 948 if (NULL != j_request_body) 949 json_decref (j_request_body); 950 if (NULL != curlh) 951 curl_easy_cleanup (curlh); 952 TALER_EXCHANGE_withdraw_blinded_cancel (wbh); 953 return; 954 #undef FAIL_IF 955 } 956 957 958 /** 959 * @brief Callback to copy the results from the call to TALER_withdraw_blinded 960 * in the non-age-restricted case to the result for the originating call from TALER_withdraw. 961 * 962 * @param cls struct TALER_WithdrawHandle 963 * @param wbr The response 964 */ 965 static void 966 copy_results ( 967 void *cls, 968 const struct TALER_EXCHANGE_WithdrawBlindedResponse *wbr) 969 { 970 /* The original handle from the top-level call to withdraw */ 971 struct TALER_EXCHANGE_WithdrawHandle *wh = cls; 972 struct TALER_EXCHANGE_WithdrawResponse resp = { 973 .hr = wbr->hr, 974 }; 975 976 wh->withdraw_blinded_handle = NULL; 977 978 /** 979 * The withdraw protocol has been performed with blinded data. 980 * Now the response can be copied as is, except for the MHD_HTTP_OK case, 981 * in which we now need to perform the unblinding. 982 */ 983 switch (wbr->hr.http_status) 984 { 985 case MHD_HTTP_OK: 986 { 987 struct TALER_EXCHANGE_WithdrawCoinPrivateDetails 988 details[GNUNET_NZL (wh->num_coins)]; 989 bool ok = true; 990 991 GNUNET_assert (wh->num_coins == wbr->details.ok.num_sigs); 992 memset (details, 993 0, 994 sizeof(details)); 995 resp.details.ok.num_sigs = wbr->details.ok.num_sigs; 996 resp.details.ok.coin_details = details; 997 resp.details.ok.planchets_h = wbr->details.ok.planchets_h; 998 for (size_t n = 0; n<wh->num_coins; n++) 999 { 1000 const struct TALER_BlindedDenominationSignature *bsig = 1001 &wbr->details.ok.blinded_denom_sigs[n]; 1002 struct CoinData *cd = &wh->coin_data[n]; 1003 struct TALER_EXCHANGE_WithdrawCoinPrivateDetails *coin = &details[n]; 1004 struct TALER_FreshCoin fresh_coin; 1005 1006 *coin = wh->coin_data[n].candidates[0].details; 1007 coin->planchet = wh->coin_data[n].planchet_details[0]; 1008 GNUNET_CRYPTO_eddsa_key_get_public ( 1009 &coin->coin_priv.eddsa_priv, 1010 &coin->coin_pub.eddsa_pub); 1011 1012 if (GNUNET_OK != 1013 TALER_planchet_to_coin (&cd->denom_pub.key, 1014 bsig, 1015 &coin->blinding_key, 1016 &coin->coin_priv, 1017 &coin->h_age_commitment, 1018 &coin->h_coin_pub, 1019 &coin->blinding_values, 1020 &fresh_coin)) 1021 { 1022 resp.hr.http_status = 0; 1023 resp.hr.ec = TALER_EC_EXCHANGE_WITHDRAW_UNBLIND_FAILURE; 1024 GNUNET_break_op (0); 1025 ok = false; 1026 break; 1027 } 1028 coin->denom_sig = fresh_coin.sig; 1029 } 1030 if (ok) 1031 { 1032 wh->callback ( 1033 wh->callback_cls, 1034 &resp); 1035 wh->callback = NULL; 1036 } 1037 for (size_t n = 0; n<wh->num_coins; n++) 1038 { 1039 struct TALER_EXCHANGE_WithdrawCoinPrivateDetails *coin = &details[n]; 1040 1041 TALER_denom_sig_free (&coin->denom_sig); 1042 } 1043 break; 1044 } 1045 case MHD_HTTP_CREATED: 1046 resp.details.created = wbr->details.created; 1047 break; 1048 1049 case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 1050 resp.details.unavailable_for_legal_reasons = 1051 wbr->details.unavailable_for_legal_reasons; 1052 break; 1053 1054 default: 1055 /* nothing to do here, .hr.ec and .hr.hint are all set already from previous response */ 1056 break; 1057 } 1058 if (NULL != wh->callback) 1059 { 1060 wh->callback ( 1061 wh->callback_cls, 1062 &resp); 1063 wh->callback = NULL; 1064 } 1065 TALER_EXCHANGE_withdraw_cancel (wh); 1066 } 1067 1068 1069 /** 1070 * @brief Callback to copy the results from the call to TALER_withdraw_blinded 1071 * in the age-restricted case to the result for the originating call from TALER_withdraw. 1072 * 1073 * @param cls struct TALER_WithdrawHandle 1074 * @param wbr The response 1075 */ 1076 static void 1077 copy_results_with_age_proof ( 1078 void *cls, 1079 const struct TALER_EXCHANGE_WithdrawBlindedResponse *wbr) 1080 { 1081 /* The original handle from the top-level call to withdraw */ 1082 struct TALER_EXCHANGE_WithdrawHandle *wh = cls; 1083 uint8_t k = wbr->details.created.noreveal_index; 1084 struct TALER_EXCHANGE_WithdrawCoinPrivateDetails details[wh->num_coins]; 1085 struct TALER_EXCHANGE_WithdrawResponse resp = { 1086 .hr = wbr->hr, 1087 }; 1088 1089 wh->withdraw_blinded_handle = NULL; 1090 1091 switch (wbr->hr.http_status) 1092 { 1093 case MHD_HTTP_OK: 1094 /* in the age-restricted case, this should not happen */ 1095 GNUNET_break_op (0); 1096 break; 1097 1098 case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 1099 resp.details.unavailable_for_legal_reasons = 1100 wbr->details.unavailable_for_legal_reasons; 1101 break; 1102 1103 case MHD_HTTP_CREATED: 1104 { 1105 GNUNET_assert (wh->num_coins == wbr->details.created.num_coins); 1106 resp.details.created = wbr->details.created; 1107 resp.details.created.coin_details = details; 1108 resp.details.created.kappa_seed = wh->kappa_seed; 1109 memset (details, 1110 0, 1111 sizeof(details)); 1112 for (size_t n = 0; n< wh->num_coins; n++) 1113 { 1114 details[n] = wh->coin_data[n].candidates[k].details; 1115 details[n].planchet = wh->coin_data[n].planchet_details[k]; 1116 } 1117 break; 1118 } 1119 1120 default: 1121 break; 1122 } 1123 1124 wh->callback ( 1125 wh->callback_cls, 1126 &resp); 1127 wh->callback = NULL; 1128 TALER_EXCHANGE_withdraw_cancel (wh); 1129 } 1130 1131 1132 /** 1133 * @brief Prepares and executes TALER_EXCHANGE_withdraw_blinded. 1134 * If there were CS-denominations involved, started once the all calls 1135 * to /blinding-prepare are done. 1136 */ 1137 static void 1138 call_withdraw_blinded ( 1139 struct TALER_EXCHANGE_WithdrawHandle *wh) 1140 { 1141 1142 GNUNET_assert (NULL == wh->blinding_prepare_handle); 1143 1144 if (! wh->with_age_proof) 1145 { 1146 struct TALER_EXCHANGE_WithdrawBlindedCoinInput input[wh->num_coins]; 1147 1148 memset (input, 1149 0, 1150 sizeof(input)); 1151 1152 /* Prepare the blinded planchets as input */ 1153 for (size_t n = 0; n < wh->num_coins; n++) 1154 { 1155 input[n].denom_pub = 1156 &wh->coin_data[n].denom_pub; 1157 input[n].planchet_details = 1158 *wh->coin_data[n].planchet_details; 1159 } 1160 1161 wh->withdraw_blinded_handle = 1162 TALER_EXCHANGE_withdraw_blinded ( 1163 wh->curl_ctx, 1164 wh->keys, 1165 wh->exchange_url, 1166 wh->reserve_priv, 1167 wh->has_blinding_seed ? &wh->blinding_seed : NULL, 1168 wh->num_coins, 1169 input, 1170 ©_results, 1171 wh); 1172 } 1173 else 1174 { /* age restricted case */ 1175 struct TALER_EXCHANGE_WithdrawBlindedAgeRestrictedCoinInput 1176 ari[wh->num_coins]; 1177 1178 memset (ari, 1179 0, 1180 sizeof(ari)); 1181 1182 /* Prepare the blinded planchets as input */ 1183 for (size_t n = 0; n < wh->num_coins; n++) 1184 { 1185 ari[n].denom_pub = &wh->coin_data[n].denom_pub; 1186 for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) 1187 ari[n].planchet_details[k] = 1188 wh->coin_data[n].planchet_details[k]; 1189 } 1190 1191 wh->withdraw_blinded_handle = 1192 TALER_EXCHANGE_withdraw_blinded_with_age_proof ( 1193 wh->curl_ctx, 1194 wh->keys, 1195 wh->exchange_url, 1196 wh->reserve_priv, 1197 wh->has_blinding_seed ? &wh->blinding_seed : NULL, 1198 wh->max_age, 1199 wh->num_coins, 1200 ari, 1201 ©_results_with_age_proof, 1202 wh); 1203 } 1204 } 1205 1206 1207 /** 1208 * @brief Function called when /blinding-prepare is finished 1209 * 1210 * @param cls the `struct BlindingPrepareClosure *` 1211 * @param bpr replies from the /blinding-prepare request 1212 */ 1213 static void 1214 blinding_prepare_done ( 1215 void *cls, 1216 const struct TALER_EXCHANGE_BlindingPrepareResponse *bpr) 1217 { 1218 struct BlindingPrepareClosure *bpcls = cls; 1219 struct TALER_EXCHANGE_WithdrawHandle *wh = bpcls->withdraw_handle; 1220 1221 wh->blinding_prepare_handle = NULL; 1222 switch (bpr->hr.http_status) 1223 { 1224 case MHD_HTTP_OK: 1225 { 1226 bool success = false; 1227 size_t num = bpr->details.ok.num_blinding_values; 1228 1229 GNUNET_assert (0 != num); 1230 GNUNET_assert (num == bpcls->num_nonces); 1231 for (size_t i = 0; i < bpcls->num_prepare_coins; i++) 1232 { 1233 struct TALER_PlanchetDetail *planchet = bpcls->coins[i].planchet; 1234 struct CoinCandidate *can = bpcls->coins[i].candidate; 1235 size_t cs_idx = bpcls->coins[i].cs_idx; 1236 1237 GNUNET_assert (NULL != can); 1238 GNUNET_assert (NULL != planchet); 1239 success = false; 1240 1241 /* Complete the initialization of the coin with CS denomination */ 1242 TALER_denom_ewv_copy ( 1243 &can->details.blinding_values, 1244 &bpr->details.ok.blinding_values[cs_idx]); 1245 1246 GNUNET_assert (GNUNET_CRYPTO_BSA_CS == 1247 can->details.blinding_values.blinding_inputs->cipher); 1248 1249 TALER_planchet_setup_coin_priv ( 1250 &can->details.secret, 1251 &can->details.blinding_values, 1252 &can->details.coin_priv); 1253 1254 TALER_planchet_blinding_secret_create ( 1255 &can->details.secret, 1256 &can->details.blinding_values, 1257 &can->details.blinding_key); 1258 1259 /* This initializes the 2nd half of the 1260 can->planchet_detail.blinded_planchet */ 1261 if (GNUNET_OK != 1262 TALER_planchet_prepare ( 1263 bpcls->coins[i].denom_pub, 1264 &can->details.blinding_values, 1265 &can->details.blinding_key, 1266 &bpcls->nonces[cs_idx], 1267 &can->details.coin_priv, 1268 &can->details.h_age_commitment, 1269 &can->details.h_coin_pub, 1270 planchet)) 1271 { 1272 GNUNET_break (0); 1273 break; 1274 } 1275 1276 TALER_coin_ev_hash (&planchet->blinded_planchet, 1277 &planchet->denom_pub_hash, 1278 &can->blinded_coin_h); 1279 success = true; 1280 } 1281 1282 /* /blinding-prepare is done, we can now perform the 1283 * actual withdraw operation */ 1284 if (success) 1285 call_withdraw_blinded (wh); 1286 goto cleanup; 1287 } 1288 default: 1289 { 1290 /* We got an error condition during blinding prepare that we need to report */ 1291 struct TALER_EXCHANGE_WithdrawResponse resp = { 1292 .hr = bpr->hr 1293 }; 1294 1295 wh->callback ( 1296 wh->callback_cls, 1297 &resp); 1298 1299 wh->callback = NULL; 1300 break; 1301 } 1302 } 1303 TALER_EXCHANGE_withdraw_cancel (wh); 1304 cleanup: 1305 GNUNET_free (bpcls->coins); 1306 GNUNET_free (bpcls->nonces); 1307 GNUNET_free (bpcls); 1308 } 1309 1310 1311 /** 1312 * @brief Prepares non age-restricted coins for the call to withdraw and 1313 * calculates the total amount with fees. 1314 * For denomination with CS as cipher, initiates the preflight to retrieve the 1315 * bpcls-parameter via /blinding-prepare. 1316 * Note that only one of the three parameters seed, tuples or secrets must not be NULL 1317 * 1318 * @param wh The handler to the withdraw 1319 * @param num_coins Number of coins to withdraw 1320 * @param max_age The maximum age to commit to 1321 * @param denoms_pub Array @e num_coins of denominations 1322 * @param seed master seed from which to derive @e num_coins secrets and blinding, if @e blinding_seed is NULL 1323 * @param blinding_seed master seed for the blinding. Might be NULL, in which case the blinding_seed is derived from @e seed 1324 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure 1325 */ 1326 static enum GNUNET_GenericReturnValue 1327 prepare_coins ( 1328 struct TALER_EXCHANGE_WithdrawHandle *wh, 1329 size_t num_coins, 1330 uint8_t max_age, 1331 const struct TALER_EXCHANGE_DenomPublicKey *denoms_pub, 1332 const struct TALER_WithdrawMasterSeedP *seed, 1333 const struct TALER_BlindingMasterSeedP *blinding_seed) 1334 { 1335 size_t cs_num = 0; 1336 struct BlindingPrepareClosure *cs_closure; 1337 uint8_t kappa; 1338 1339 #define FAIL_IF(cond) \ 1340 do \ 1341 { \ 1342 if ((cond)) \ 1343 { \ 1344 GNUNET_break (! (cond)); \ 1345 goto ERROR; \ 1346 } \ 1347 } while (0) 1348 1349 GNUNET_assert (0 < num_coins); 1350 1351 wh->num_coins = num_coins; 1352 wh->max_age = max_age; 1353 wh->age_mask = denoms_pub[0].key.age_mask; 1354 wh->coin_data = GNUNET_new_array ( 1355 wh->num_coins, 1356 struct CoinData); 1357 1358 /* First, figure out how many Clause-Schnorr denominations we have */ 1359 for (size_t i =0; i< wh->num_coins; i++) 1360 { 1361 if (GNUNET_CRYPTO_BSA_CS == 1362 denoms_pub[i].key.bsign_pub_key->cipher) 1363 cs_num++; 1364 } 1365 1366 if (wh->with_age_proof) 1367 { 1368 kappa = TALER_CNC_KAPPA; 1369 } 1370 else 1371 { 1372 kappa = 1; 1373 } 1374 1375 { 1376 struct TALER_PlanchetMasterSecretP secrets[kappa][num_coins]; 1377 struct TALER_EXCHANGE_NonceKey cs_nonce_keys[GNUNET_NZL (cs_num)]; 1378 uint32_t cs_indices[GNUNET_NZL (cs_num)]; 1379 1380 size_t cs_denom_idx = 0; 1381 size_t cs_coin_idx = 0; 1382 1383 if (wh->with_age_proof) 1384 { 1385 TALER_withdraw_expand_kappa_seed (seed, 1386 &wh->kappa_seed); 1387 for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) 1388 { 1389 TALER_withdraw_expand_secrets ( 1390 num_coins, 1391 &wh->kappa_seed.tuple[k], 1392 secrets[k]); 1393 } 1394 } 1395 else 1396 { 1397 TALER_withdraw_expand_secrets ( 1398 num_coins, 1399 seed, 1400 secrets[0]); 1401 } 1402 1403 if (0 < cs_num) 1404 { 1405 memset (cs_nonce_keys, 1406 0, 1407 sizeof(cs_nonce_keys)); 1408 cs_closure = GNUNET_new (struct BlindingPrepareClosure); 1409 cs_closure->withdraw_handle = wh; 1410 cs_closure->num_prepare_coins = cs_num * kappa; 1411 GNUNET_assert ((1 == kappa) || (cs_num * kappa > cs_num)); 1412 cs_closure->coins = 1413 GNUNET_new_array (cs_closure->num_prepare_coins, 1414 struct BlindingPrepareCoinData); 1415 cs_closure->num_nonces = cs_num; 1416 cs_closure->nonces = 1417 GNUNET_new_array (cs_closure->num_nonces, 1418 union GNUNET_CRYPTO_BlindSessionNonce); 1419 } 1420 else 1421 { 1422 cs_closure = NULL; 1423 } 1424 1425 for (uint32_t i = 0; i < wh->num_coins; i++) 1426 { 1427 struct CoinData *cd = &wh->coin_data[i]; 1428 bool age_denom = (0 != denoms_pub[i].key.age_mask.bits); 1429 1430 cd->denom_pub = denoms_pub[i]; 1431 /* The age mask must be the same for all coins */ 1432 FAIL_IF (wh->with_age_proof && 1433 (0 == denoms_pub[i].key.age_mask.bits)); 1434 FAIL_IF (wh->age_mask.bits != 1435 denoms_pub[i].key.age_mask.bits); 1436 TALER_denom_pub_copy (&cd->denom_pub.key, 1437 &denoms_pub[i].key); 1438 1439 /* Mark the indices of the coins which are of type Clause-Schnorr 1440 * and add their denomination public key hash to the list. 1441 */ 1442 if (GNUNET_CRYPTO_BSA_CS == 1443 cd->denom_pub.key.bsign_pub_key->cipher) 1444 { 1445 GNUNET_assert (cs_denom_idx<cs_num); 1446 cs_indices[cs_denom_idx] = i; 1447 cs_nonce_keys[cs_denom_idx].cnc_num = i; 1448 cs_nonce_keys[cs_denom_idx].pk = &cd->denom_pub; 1449 cs_denom_idx++; 1450 } 1451 1452 /* 1453 * Note that we "loop" here either only once (if with_age_proof is false), 1454 * or TALER_CNC_KAPPA times. 1455 */ 1456 for (uint8_t k = 0; k < kappa; k++) 1457 { 1458 struct CoinCandidate *can = &cd->candidates[k]; 1459 struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k]; 1460 1461 can->details.secret = secrets[k][i]; 1462 /* 1463 * The age restriction needs to be set on a coin if the denomination 1464 * support age restriction. Note that his is regardless of weither 1465 * with_age_proof is set or not. 1466 */ 1467 if (age_denom) 1468 { 1469 /* Derive the age restriction from the given secret and 1470 * the maximum age */ 1471 TALER_age_restriction_from_secret ( 1472 &can->details.secret, 1473 &wh->age_mask, 1474 wh->max_age, 1475 &can->details.age_commitment_proof); 1476 1477 TALER_age_commitment_hash ( 1478 &can->details.age_commitment_proof.commitment, 1479 &can->details.h_age_commitment); 1480 } 1481 1482 switch (cd->denom_pub.key.bsign_pub_key->cipher) 1483 { 1484 case GNUNET_CRYPTO_BSA_RSA: 1485 TALER_denom_ewv_copy (&can->details.blinding_values, 1486 TALER_denom_ewv_rsa_singleton ()); 1487 TALER_planchet_setup_coin_priv (&can->details.secret, 1488 &can->details.blinding_values, 1489 &can->details.coin_priv); 1490 TALER_planchet_blinding_secret_create (&can->details.secret, 1491 &can->details.blinding_values, 1492 &can->details.blinding_key); 1493 FAIL_IF (GNUNET_OK != 1494 TALER_planchet_prepare (&cd->denom_pub.key, 1495 &can->details.blinding_values, 1496 &can->details.blinding_key, 1497 NULL, 1498 &can->details.coin_priv, 1499 (age_denom) 1500 ? &can->details.h_age_commitment 1501 : NULL, 1502 &can->details.h_coin_pub, 1503 planchet)); 1504 TALER_coin_ev_hash (&planchet->blinded_planchet, 1505 &planchet->denom_pub_hash, 1506 &can->blinded_coin_h); 1507 1508 break; 1509 1510 case GNUNET_CRYPTO_BSA_CS: 1511 { 1512 /** 1513 * Prepare the nonce and save the index and the denomination for the callback 1514 * after the call to blinding-prepare 1515 */ 1516 cs_closure->coins[cs_coin_idx].candidate = can; 1517 cs_closure->coins[cs_coin_idx].planchet = planchet; 1518 cs_closure->coins[cs_coin_idx].denom_pub = &cd->denom_pub.key; 1519 cs_closure->coins[cs_coin_idx].cs_idx = i; 1520 cs_closure->coins[cs_coin_idx].age_denom = age_denom; 1521 cs_coin_idx++; 1522 break; 1523 } 1524 default: 1525 FAIL_IF (1); 1526 } 1527 } 1528 } 1529 1530 if (0 < cs_num) 1531 { 1532 if (NULL != blinding_seed) 1533 { 1534 wh->blinding_seed = *blinding_seed; 1535 } 1536 else 1537 { 1538 TALER_cs_withdraw_seed_to_blinding_seed ( 1539 seed, 1540 &wh->blinding_seed); 1541 } 1542 wh->has_blinding_seed = true; 1543 1544 TALER_cs_derive_only_cs_blind_nonces_from_seed ( 1545 &wh->blinding_seed, 1546 false, /* not for melt */ 1547 cs_num, 1548 cs_indices, 1549 cs_closure->nonces); 1550 1551 wh->blinding_prepare_handle = 1552 TALER_EXCHANGE_blinding_prepare_for_withdraw ( 1553 wh->curl_ctx, 1554 wh->exchange_url, 1555 &wh->blinding_seed, 1556 cs_num, 1557 cs_nonce_keys, 1558 &blinding_prepare_done, 1559 cs_closure); 1560 FAIL_IF (NULL == wh->blinding_prepare_handle); 1561 } 1562 } 1563 return GNUNET_OK; 1564 1565 ERROR: 1566 if (0<cs_num) 1567 { 1568 GNUNET_free (cs_closure->nonces); 1569 GNUNET_free (cs_closure); 1570 } 1571 TALER_EXCHANGE_withdraw_cancel (wh); 1572 return GNUNET_SYSERR; 1573 #undef FAIL_IF 1574 1575 } 1576 1577 1578 /** 1579 * Prepare a withdraw handle for both, the non-restricted 1580 * and age-restricted case. 1581 * 1582 * @param curl_ctx The curl context to use 1583 * @param keys The keys from the exchange 1584 * @param exchange_url The base url to the exchange 1585 * @param reserve_priv The private key of the exchange 1586 * @param res_cb The callback to call on response 1587 * @param res_cb_cls The closure to pass to the callback 1588 */ 1589 static struct TALER_EXCHANGE_WithdrawHandle * 1590 setup_withdraw_handle ( 1591 struct GNUNET_CURL_Context *curl_ctx, 1592 struct TALER_EXCHANGE_Keys *keys, 1593 const char *exchange_url, 1594 const struct TALER_ReservePrivateKeyP *reserve_priv, 1595 TALER_EXCHANGE_WithdrawCallback res_cb, 1596 void *res_cb_cls) 1597 { 1598 struct TALER_EXCHANGE_WithdrawHandle *wh; 1599 1600 wh = GNUNET_new (struct TALER_EXCHANGE_WithdrawHandle); 1601 wh->exchange_url = exchange_url; 1602 wh->keys = TALER_EXCHANGE_keys_incref (keys); 1603 wh->curl_ctx = curl_ctx; 1604 wh->reserve_priv = reserve_priv; 1605 wh->callback = res_cb; 1606 wh->callback_cls = res_cb_cls; 1607 1608 return wh; 1609 } 1610 1611 1612 struct TALER_EXCHANGE_WithdrawHandle * 1613 TALER_EXCHANGE_withdraw_extra_blinding_seed ( 1614 struct GNUNET_CURL_Context *curl_ctx, 1615 struct TALER_EXCHANGE_Keys *keys, 1616 const char *exchange_url, 1617 const struct TALER_ReservePrivateKeyP *reserve_priv, 1618 size_t num_coins, 1619 const struct TALER_EXCHANGE_DenomPublicKey denoms_pub[static num_coins], 1620 const struct TALER_WithdrawMasterSeedP *seed, 1621 const struct TALER_BlindingMasterSeedP *blinding_seed, 1622 uint8_t opaque_max_age, 1623 TALER_EXCHANGE_WithdrawCallback res_cb, 1624 void *res_cb_cls) 1625 { 1626 struct TALER_EXCHANGE_WithdrawHandle *wh; 1627 1628 wh = setup_withdraw_handle (curl_ctx, 1629 keys, 1630 exchange_url, 1631 reserve_priv, 1632 res_cb, 1633 res_cb_cls); 1634 GNUNET_assert (NULL != wh); 1635 wh->with_age_proof = false; 1636 1637 if (GNUNET_OK != 1638 prepare_coins (wh, 1639 num_coins, 1640 opaque_max_age, 1641 denoms_pub, 1642 seed, 1643 blinding_seed)) 1644 { 1645 GNUNET_free (wh); 1646 return NULL; 1647 } 1648 1649 /* If there were no CS denominations, we can now perform the actual 1650 * withdraw protocol. Otherwise, there are calls to /blinding-prepare 1651 * in flight and once they finish, the withdraw-protocol will be 1652 * called from within the blinding_prepare_done-function. 1653 */ 1654 if (NULL == wh->blinding_prepare_handle) 1655 call_withdraw_blinded (wh); 1656 1657 return wh; 1658 } 1659 1660 1661 struct TALER_EXCHANGE_WithdrawHandle * 1662 TALER_EXCHANGE_withdraw ( 1663 struct GNUNET_CURL_Context *curl_ctx, 1664 struct TALER_EXCHANGE_Keys *keys, 1665 const char *exchange_url, 1666 const struct TALER_ReservePrivateKeyP *reserve_priv, 1667 size_t num_coins, 1668 const struct TALER_EXCHANGE_DenomPublicKey denoms_pub[static num_coins], 1669 const struct TALER_WithdrawMasterSeedP *seed, 1670 uint8_t opaque_max_age, 1671 TALER_EXCHANGE_WithdrawCallback res_cb, 1672 void *res_cb_cls) 1673 { 1674 return TALER_EXCHANGE_withdraw_extra_blinding_seed ( 1675 curl_ctx, 1676 keys, 1677 exchange_url, 1678 reserve_priv, 1679 num_coins, 1680 denoms_pub, 1681 seed, 1682 NULL, 1683 opaque_max_age, 1684 res_cb, 1685 res_cb_cls 1686 ); 1687 } 1688 1689 1690 struct TALER_EXCHANGE_WithdrawHandle * 1691 TALER_EXCHANGE_withdraw_with_age_proof_extra_blinding_seed ( 1692 struct GNUNET_CURL_Context *curl_ctx, 1693 struct TALER_EXCHANGE_Keys *keys, 1694 const char *exchange_url, 1695 const struct TALER_ReservePrivateKeyP *reserve_priv, 1696 size_t num_coins, 1697 const struct TALER_EXCHANGE_DenomPublicKey denoms_pub[static num_coins], 1698 const struct TALER_WithdrawMasterSeedP *seed, 1699 const struct TALER_BlindingMasterSeedP *blinding_seed, 1700 uint8_t max_age, 1701 TALER_EXCHANGE_WithdrawCallback res_cb, 1702 void *res_cb_cls) 1703 { 1704 struct TALER_EXCHANGE_WithdrawHandle *wh; 1705 1706 wh = setup_withdraw_handle (curl_ctx, 1707 keys, 1708 exchange_url, 1709 reserve_priv, 1710 res_cb, 1711 res_cb_cls); 1712 GNUNET_assert (NULL != wh); 1713 1714 wh->with_age_proof = true; 1715 1716 if (GNUNET_OK != 1717 prepare_coins (wh, 1718 num_coins, 1719 max_age, 1720 denoms_pub, 1721 seed, 1722 blinding_seed)) 1723 { 1724 GNUNET_free (wh); 1725 return NULL; 1726 } 1727 1728 /* If there were no CS denominations, we can now perform the actual 1729 * withdraw protocol. Otherwise, there are calls to /blinding-prepare 1730 * in flight and once they finish, the withdraw-protocol will be 1731 * called from within the blinding_prepare_done-function. 1732 */ 1733 if (NULL == wh->blinding_prepare_handle) 1734 call_withdraw_blinded (wh); 1735 1736 return wh; 1737 } 1738 1739 1740 struct TALER_EXCHANGE_WithdrawHandle * 1741 TALER_EXCHANGE_withdraw_with_age_proof ( 1742 struct GNUNET_CURL_Context *curl_ctx, 1743 struct TALER_EXCHANGE_Keys *keys, 1744 const char *exchange_url, 1745 const struct TALER_ReservePrivateKeyP *reserve_priv, 1746 size_t num_coins, 1747 const struct TALER_EXCHANGE_DenomPublicKey denoms_pub[static num_coins], 1748 const struct TALER_WithdrawMasterSeedP *seed, 1749 uint8_t max_age, 1750 TALER_EXCHANGE_WithdrawCallback res_cb, 1751 void *res_cb_cls) 1752 { 1753 return TALER_EXCHANGE_withdraw_with_age_proof_extra_blinding_seed ( 1754 curl_ctx, 1755 keys, 1756 exchange_url, 1757 reserve_priv, 1758 num_coins, 1759 denoms_pub, 1760 seed, 1761 NULL, 1762 max_age, 1763 res_cb, 1764 res_cb_cls); 1765 } 1766 1767 1768 void 1769 TALER_EXCHANGE_withdraw_cancel ( 1770 struct TALER_EXCHANGE_WithdrawHandle *wh) 1771 { 1772 uint8_t kappa = wh->with_age_proof ? TALER_CNC_KAPPA : 1; 1773 1774 /* Cleanup coin data */ 1775 for (unsigned int i = 0; i<wh->num_coins; i++) 1776 { 1777 struct CoinData *cd = &wh->coin_data[i]; 1778 1779 for (uint8_t k = 0; k < kappa; k++) 1780 { 1781 struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k]; 1782 struct CoinCandidate *can = &cd->candidates[k]; 1783 1784 TALER_blinded_planchet_free (&planchet->blinded_planchet); 1785 TALER_denom_ewv_free (&can->details.blinding_values); 1786 TALER_age_commitment_proof_free (&can->details.age_commitment_proof); 1787 } 1788 TALER_denom_pub_free (&cd->denom_pub.key); 1789 } 1790 1791 TALER_EXCHANGE_blinding_prepare_cancel (wh->blinding_prepare_handle); 1792 TALER_EXCHANGE_withdraw_blinded_cancel (wh->withdraw_blinded_handle); 1793 wh->blinding_prepare_handle = NULL; 1794 wh->withdraw_blinded_handle = NULL; 1795 1796 GNUNET_free (wh->coin_data); 1797 TALER_EXCHANGE_keys_decref (wh->keys); 1798 GNUNET_free (wh); 1799 } 1800 1801 1802 /** 1803 * @brief Prepare the handler for blinded withdraw 1804 * 1805 * Allocates the handler struct and prepares all fields of the handler 1806 * except the blinded planchets, 1807 * which depend on them being age-restricted or not. 1808 * 1809 * @param curl_ctx the context for curl 1810 * @param keys the exchange keys 1811 * @param exchange_url the url to the exchange 1812 * @param reserve_priv the reserve's private key 1813 * @param res_cb the callback on result 1814 * @param res_cb_cls the closure to pass on to the callback 1815 * @return the handler 1816 */ 1817 static struct TALER_EXCHANGE_WithdrawBlindedHandle * 1818 setup_handler_common ( 1819 struct GNUNET_CURL_Context *curl_ctx, 1820 struct TALER_EXCHANGE_Keys *keys, 1821 const char *exchange_url, 1822 const struct TALER_ReservePrivateKeyP *reserve_priv, 1823 TALER_EXCHANGE_WithdrawBlindedCallback res_cb, 1824 void *res_cb_cls) 1825 { 1826 1827 struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh = 1828 GNUNET_new (struct TALER_EXCHANGE_WithdrawBlindedHandle); 1829 1830 wbh->keys = TALER_EXCHANGE_keys_incref (keys); 1831 wbh->curl_ctx = curl_ctx; 1832 wbh->reserve_priv = reserve_priv; 1833 wbh->callback = res_cb; 1834 wbh->callback_cls = res_cb_cls; 1835 wbh->request_url = TALER_url_join (exchange_url, 1836 "withdraw", 1837 NULL); 1838 GNUNET_CRYPTO_eddsa_key_get_public ( 1839 &wbh->reserve_priv->eddsa_priv, 1840 &wbh->reserve_pub.eddsa_pub); 1841 1842 return wbh; 1843 } 1844 1845 1846 struct TALER_EXCHANGE_WithdrawBlindedHandle * 1847 TALER_EXCHANGE_withdraw_blinded ( 1848 struct GNUNET_CURL_Context *curl_ctx, 1849 struct TALER_EXCHANGE_Keys *keys, 1850 const char *exchange_url, 1851 const struct TALER_ReservePrivateKeyP *reserve_priv, 1852 const struct TALER_BlindingMasterSeedP *blinding_seed, 1853 size_t num_input, 1854 const struct TALER_EXCHANGE_WithdrawBlindedCoinInput 1855 blinded_input[static num_input], 1856 TALER_EXCHANGE_WithdrawBlindedCallback res_cb, 1857 void *res_cb_cls) 1858 { 1859 struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh = 1860 setup_handler_common (curl_ctx, 1861 keys, 1862 exchange_url, 1863 reserve_priv, 1864 res_cb, 1865 res_cb_cls); 1866 1867 wbh->with_age_proof = false; 1868 wbh->num_input = num_input; 1869 wbh->blinded.input = blinded_input; 1870 wbh->blinding_seed = blinding_seed; 1871 1872 perform_withdraw_protocol (wbh); 1873 return wbh; 1874 } 1875 1876 1877 struct TALER_EXCHANGE_WithdrawBlindedHandle * 1878 TALER_EXCHANGE_withdraw_blinded_with_age_proof ( 1879 struct GNUNET_CURL_Context *curl_ctx, 1880 struct TALER_EXCHANGE_Keys *keys, 1881 const char *exchange_url, 1882 const struct TALER_ReservePrivateKeyP *reserve_priv, 1883 const struct TALER_BlindingMasterSeedP *blinding_seed, 1884 uint8_t max_age, 1885 unsigned int num_input, 1886 const struct TALER_EXCHANGE_WithdrawBlindedAgeRestrictedCoinInput 1887 blinded_input[static num_input], 1888 TALER_EXCHANGE_WithdrawBlindedCallback res_cb, 1889 void *res_cb_cls) 1890 { 1891 struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh = 1892 setup_handler_common (curl_ctx, 1893 keys, 1894 exchange_url, 1895 reserve_priv, 1896 res_cb, 1897 res_cb_cls); 1898 1899 wbh->with_age_proof = true; 1900 wbh->max_age = max_age; 1901 wbh->num_input = num_input; 1902 wbh->blinded.with_age_proof_input = blinded_input; 1903 wbh->blinding_seed = blinding_seed; 1904 1905 perform_withdraw_protocol (wbh); 1906 return wbh; 1907 } 1908 1909 1910 void 1911 TALER_EXCHANGE_withdraw_blinded_cancel ( 1912 struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh) 1913 { 1914 if (NULL == wbh) 1915 return; 1916 if (NULL != wbh->job) 1917 { 1918 GNUNET_CURL_job_cancel (wbh->job); 1919 wbh->job = NULL; 1920 } 1921 GNUNET_free (wbh->request_url); 1922 TALER_EXCHANGE_keys_decref (wbh->keys); 1923 TALER_curl_easy_post_finished (&wbh->post_ctx); 1924 GNUNET_free (wbh); 1925 } 1926 1927 1928 /* exchange_api_withdraw.c */