exchange_api_post-withdraw.c (32704B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2023-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 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file lib/exchange_api_post-withdraw.c 19 * @brief Implementation of /withdraw requests 20 * @author Özgür Kesim 21 */ 22 #include "taler/platform.h" 23 #include <gnunet/gnunet_common.h> 24 #include <jansson.h> 25 #include <microhttpd.h> /* just for HTTP status codes */ 26 #include <gnunet/gnunet_util_lib.h> 27 #include <gnunet/gnunet_json_lib.h> 28 #include <gnunet/gnunet_curl_lib.h> 29 #include <sys/wait.h> 30 #include "taler/taler_curl_lib.h" 31 #include "taler/taler_error_codes.h" 32 #include "taler/taler_json_lib.h" 33 #include "taler/taler_exchange_service.h" 34 #include "exchange_api_common.h" 35 #include "exchange_api_handle.h" 36 #include "taler/taler_signatures.h" 37 #include "exchange_api_curl_defaults.h" 38 #include "taler/taler_util.h" 39 40 /** 41 * A CoinCandidate is populated from a master secret. 42 * The data is copied from and generated out of the client's input. 43 */ 44 struct CoinCandidate 45 { 46 /** 47 * The details derived form the master secrets 48 */ 49 struct TALER_EXCHANGE_WithdrawCoinPrivateDetails details; 50 51 /** 52 * Blinded hash of the coin 53 **/ 54 struct TALER_BlindedCoinHashP blinded_coin_h; 55 56 }; 57 58 59 /** 60 * Data we keep per coin in the batch. 61 * This is copied from and generated out of the input provided 62 * by the client. 63 */ 64 struct CoinData 65 { 66 /** 67 * The denomination of the coin. 68 */ 69 struct TALER_EXCHANGE_DenomPublicKey denom_pub; 70 71 /** 72 * The Candidates for the coin. If the batch is not age-restricted, 73 * only index 0 is used. 74 */ 75 struct CoinCandidate candidates[TALER_CNC_KAPPA]; 76 77 /** 78 * Details of the planchet(s). If the batch is not age-restricted, 79 * only index 0 is used. 80 */ 81 struct TALER_PlanchetDetail planchet_details[TALER_CNC_KAPPA]; 82 }; 83 84 85 /** 86 * Per-CS-coin data needed to complete the coin after /blinding-prepare. 87 */ 88 struct BlindingPrepareCoinData 89 { 90 /** 91 * Pointer to the candidate in CoinData.candidates, 92 * to continue to build its contents based on the results from /blinding-prepare 93 */ 94 struct CoinCandidate *candidate; 95 96 /** 97 * Planchet to finally generate in the corresponding candidate 98 * in CoinData.planchet_details 99 */ 100 struct TALER_PlanchetDetail *planchet; 101 102 /** 103 * Denomination information, needed for the 104 * step after /blinding-prepare 105 */ 106 const struct TALER_DenominationPublicKey *denom_pub; 107 108 /** 109 * True, if denomination supports age restriction 110 */ 111 bool age_denom; 112 113 /** 114 * The index into the array of returned values from the call to 115 * /blinding-prepare that are to be used for this coin. 116 */ 117 size_t cs_idx; 118 119 }; 120 121 122 /** 123 * A /withdraw request-handle for calls from 124 * a wallet, i. e. when blinding data is available. 125 */ 126 struct TALER_EXCHANGE_PostWithdrawHandle 127 { 128 129 /** 130 * The base-URL of the exchange. 131 */ 132 const char *exchange_url; 133 134 /** 135 * Seed to derive of all seeds for the coins. 136 */ 137 struct TALER_WithdrawMasterSeedP seed; 138 139 /** 140 * If @e with_age_proof is true, the derived TALER_CNC_KAPPA many 141 * seeds for candidate batches. 142 */ 143 struct TALER_KappaWithdrawMasterSeedP kappa_seed; 144 145 /** 146 * True if @e blinding_seed is filled, that is, if 147 * any of the denominations is of cipher type CS 148 */ 149 bool has_blinding_seed; 150 151 /** 152 * Seed used for the derivation of blinding factors for denominations 153 * with Clause-Schnorr cipher. We derive this from the master seed 154 * for the withdraw, but independent from the other planchet seeds. 155 * Only valid when @e has_blinding_seed is true; 156 */ 157 struct TALER_BlindingMasterSeedP blinding_seed; 158 159 /** 160 * Reserve private key. 161 */ 162 const struct TALER_ReservePrivateKeyP *reserve_priv; 163 164 /** 165 * Reserve public key, calculated 166 */ 167 struct TALER_ReservePublicKeyP reserve_pub; 168 169 /** 170 * Signature of the reserve for the request, calculated after all 171 * parameters for the coins are collected. 172 */ 173 struct TALER_ReserveSignatureP reserve_sig; 174 175 /* 176 * The denomination keys of the exchange 177 */ 178 struct TALER_EXCHANGE_Keys *keys; 179 180 /** 181 * True, if the withdraw is for age-restricted coins, with age-proof. 182 * The denominations MUST support age restriction. 183 */ 184 bool with_age_proof; 185 186 /** 187 * If @e with_age_proof is true, the age mask, extracted 188 * from the denominations. 189 * MUST be the same for all denominations. 190 */ 191 struct TALER_AgeMask age_mask; 192 193 /** 194 * The maximum age to commit to. If @e with_age_proof 195 * is true, the client will need to proof the correct setting 196 * of age-restriction on the coins via an additional call 197 * to /reveal-withdraw. 198 */ 199 uint8_t max_age; 200 201 /** 202 * Length of the @e coin_data Array 203 */ 204 size_t num_coins; 205 206 /** 207 * Array of per-coin data 208 */ 209 struct CoinData *coin_data; 210 211 /** 212 * Context for curl. 213 */ 214 struct GNUNET_CURL_Context *curl_ctx; 215 216 /** 217 * Function to call with withdraw response results. 218 */ 219 TALER_EXCHANGE_PostWithdrawCallback callback; 220 221 /** 222 * Closure for @e callback 223 */ 224 void *callback_cls; 225 226 /** 227 * The handler for the call to /blinding-prepare, needed for CS denominations. 228 * NULL until _start is called for CS denominations, or when no CS denoms. 229 */ 230 struct TALER_EXCHANGE_PostBlindingPrepareHandle *blinding_prepare_handle; 231 232 /** 233 * The Handler for the actual call to the exchange 234 */ 235 struct TALER_EXCHANGE_PostWithdrawBlindedHandle *withdraw_blinded_handle; 236 237 /** 238 * Number of CS denomination coin entries in @e bp_coins. 239 * Zero if no CS denominations. 240 */ 241 size_t num_bp_coins; 242 243 /** 244 * Array of @e num_bp_coins coin data for the blinding-prepare step. 245 */ 246 struct BlindingPrepareCoinData *bp_coins; 247 248 /** 249 * Number of nonces in @e bp_nonces. 250 */ 251 size_t num_bp_nonces; 252 253 /** 254 * Array of @e num_bp_nonces nonces for CS denominations. 255 */ 256 union GNUNET_CRYPTO_BlindSessionNonce *bp_nonces; 257 258 /** 259 * Nonce keys for the blinding-prepare call. 260 */ 261 struct TALER_EXCHANGE_NonceKey *bp_nonce_keys; 262 263 /** 264 * Number of nonce keys in @e bp_nonce_keys. 265 */ 266 size_t num_bp_nonce_keys; 267 268 /** 269 * Array of @e init_num_coins denomination public keys. 270 * NULL after _start is called. 271 */ 272 struct TALER_EXCHANGE_DenomPublicKey *init_denoms_pub; 273 274 /** 275 * Number of coins provided in @e init_denoms_pub. 276 */ 277 size_t init_num_coins; 278 279 struct 280 { 281 282 /** 283 * True if @e blinding_seed is filled, that is, if 284 * any of the denominations is of cipher type CS 285 */ 286 bool has_blinding_seed; 287 288 /** 289 * Seed used for the derivation of blinding factors for denominations 290 * with Clause-Schnorr cipher. We derive this from the master seed 291 * for the withdraw, but independent from the other planchet seeds. 292 * Only valid when @e has_blinding_seed is true; 293 */ 294 struct TALER_BlindingMasterSeedP blinding_seed; 295 296 } options; 297 }; 298 299 300 /** 301 * @brief Callback to copy the results from the call to post_withdraw_blinded 302 * in the non-age-restricted case to the result for the originating call. 303 * 304 * @param cls struct TALER_EXCHANGE_PostWithdrawHandle 305 * @param wbr The response 306 */ 307 static void 308 copy_results ( 309 void *cls, 310 const struct TALER_EXCHANGE_PostWithdrawBlindedResponse *wbr) 311 { 312 /* The original handle from the top-level call to withdraw */ 313 struct TALER_EXCHANGE_PostWithdrawHandle *wh = cls; 314 struct TALER_EXCHANGE_PostWithdrawResponse resp = { 315 .hr = wbr->hr, 316 }; 317 318 wh->withdraw_blinded_handle = NULL; 319 320 /** 321 * The withdraw protocol has been performed with blinded data. 322 * Now the response can be copied as is, except for the MHD_HTTP_OK case, 323 * in which we now need to perform the unblinding. 324 */ 325 switch (wbr->hr.http_status) 326 { 327 case MHD_HTTP_OK: 328 { 329 struct TALER_EXCHANGE_WithdrawCoinPrivateDetails 330 details[GNUNET_NZL (wh->num_coins)]; 331 bool ok = true; 332 333 GNUNET_assert (wh->num_coins == wbr->details.ok.num_sigs); 334 memset (details, 335 0, 336 sizeof(details)); 337 resp.details.ok.num_sigs = wbr->details.ok.num_sigs; 338 resp.details.ok.coin_details = details; 339 resp.details.ok.planchets_h = wbr->details.ok.planchets_h; 340 for (size_t n = 0; n<wh->num_coins; n++) 341 { 342 const struct TALER_BlindedDenominationSignature *bsig = 343 &wbr->details.ok.blinded_denom_sigs[n]; 344 struct CoinData *cd = &wh->coin_data[n]; 345 struct TALER_EXCHANGE_WithdrawCoinPrivateDetails *coin = &details[n]; 346 struct TALER_FreshCoin fresh_coin; 347 348 *coin = wh->coin_data[n].candidates[0].details; 349 coin->planchet = wh->coin_data[n].planchet_details[0]; 350 GNUNET_CRYPTO_eddsa_key_get_public ( 351 &coin->coin_priv.eddsa_priv, 352 &coin->coin_pub.eddsa_pub); 353 354 if (GNUNET_OK != 355 TALER_planchet_to_coin (&cd->denom_pub.key, 356 bsig, 357 &coin->blinding_key, 358 &coin->coin_priv, 359 &coin->h_age_commitment, 360 &coin->h_coin_pub, 361 &coin->blinding_values, 362 &fresh_coin)) 363 { 364 resp.hr.http_status = 0; 365 resp.hr.ec = TALER_EC_EXCHANGE_WITHDRAW_UNBLIND_FAILURE; 366 GNUNET_break_op (0); 367 ok = false; 368 break; 369 } 370 coin->denom_sig = fresh_coin.sig; 371 } 372 if (ok) 373 { 374 wh->callback ( 375 wh->callback_cls, 376 &resp); 377 wh->callback = NULL; 378 } 379 for (size_t n = 0; n<wh->num_coins; n++) 380 { 381 struct TALER_EXCHANGE_WithdrawCoinPrivateDetails *coin = &details[n]; 382 383 TALER_denom_sig_free (&coin->denom_sig); 384 } 385 break; 386 } 387 case MHD_HTTP_CREATED: 388 resp.details.created = wbr->details.created; 389 break; 390 case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 391 resp.details.unavailable_for_legal_reasons = 392 wbr->details.unavailable_for_legal_reasons; 393 break; 394 395 default: 396 /* nothing to do here, .hr.ec and .hr.hint are all set already from previous response */ 397 break; 398 } 399 if (NULL != wh->callback) 400 { 401 wh->callback ( 402 wh->callback_cls, 403 &resp); 404 wh->callback = NULL; 405 } 406 TALER_EXCHANGE_post_withdraw_cancel (wh); 407 } 408 409 410 /** 411 * @brief Callback to copy the results from the call to post_withdraw_blinded 412 * in the age-restricted case. 413 * 414 * @param cls struct TALER_EXCHANGE_PostWithdrawHandle 415 * @param wbr The response 416 */ 417 static void 418 copy_results_with_age_proof ( 419 void *cls, 420 const struct TALER_EXCHANGE_PostWithdrawBlindedResponse *wbr) 421 { 422 /* The original handle from the top-level call to withdraw */ 423 struct TALER_EXCHANGE_PostWithdrawHandle *wh = cls; 424 uint8_t k = wbr->details.created.noreveal_index; 425 struct TALER_EXCHANGE_WithdrawCoinPrivateDetails details[wh->num_coins]; 426 struct TALER_EXCHANGE_PostWithdrawResponse resp = { 427 .hr = wbr->hr, 428 }; 429 430 wh->withdraw_blinded_handle = NULL; 431 switch (wbr->hr.http_status) 432 { 433 case MHD_HTTP_OK: 434 /* in the age-restricted case, this should not happen */ 435 GNUNET_break_op (0); 436 break; 437 case MHD_HTTP_CREATED: 438 { 439 GNUNET_assert (wh->num_coins == wbr->details.created.num_coins); 440 resp.details.created = wbr->details.created; 441 resp.details.created.coin_details = details; 442 resp.details.created.kappa_seed = wh->kappa_seed; 443 memset (details, 444 0, 445 sizeof(details)); 446 for (size_t n = 0; n< wh->num_coins; n++) 447 { 448 details[n] = wh->coin_data[n].candidates[k].details; 449 details[n].planchet = wh->coin_data[n].planchet_details[k]; 450 } 451 break; 452 } 453 case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 454 resp.details.unavailable_for_legal_reasons = 455 wbr->details.unavailable_for_legal_reasons; 456 break; 457 default: 458 break; 459 } 460 461 wh->callback ( 462 wh->callback_cls, 463 &resp); 464 wh->callback = NULL; 465 TALER_EXCHANGE_post_withdraw_cancel (wh); 466 } 467 468 469 /** 470 * @brief Prepares and starts the actual TALER_EXCHANGE_post_withdraw_blinded 471 * operation once all blinding-prepare steps are done (or immediately if 472 * there are no CS denominations). 473 * 474 * @param wh The withdraw handle 475 * @return #TALER_EC_NONE on success, error code on failure 476 */ 477 static enum TALER_ErrorCode 478 call_withdraw_blinded ( 479 struct TALER_EXCHANGE_PostWithdrawHandle *wh) 480 { 481 enum TALER_ErrorCode ec; 482 483 GNUNET_assert (NULL == wh->blinding_prepare_handle); 484 485 if (! wh->with_age_proof) 486 { 487 struct TALER_EXCHANGE_WithdrawBlindedCoinInput input[wh->num_coins]; 488 489 memset (input, 490 0, 491 sizeof(input)); 492 493 /* Prepare the blinded planchets as input */ 494 for (size_t n = 0; n < wh->num_coins; n++) 495 { 496 input[n].denom_pub = 497 &wh->coin_data[n].denom_pub; 498 input[n].planchet_details = 499 *wh->coin_data[n].planchet_details; 500 } 501 502 wh->withdraw_blinded_handle = 503 TALER_EXCHANGE_post_withdraw_blinded_create ( 504 wh->curl_ctx, 505 wh->keys, 506 wh->exchange_url, 507 wh->reserve_priv, 508 wh->has_blinding_seed ? &wh->blinding_seed : NULL, 509 wh->num_coins, 510 input); 511 if (NULL == wh->withdraw_blinded_handle) 512 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 513 ec = TALER_EXCHANGE_post_withdraw_blinded_start ( 514 wh->withdraw_blinded_handle, 515 ©_results, 516 wh); 517 if (TALER_EC_NONE != ec) 518 { 519 wh->withdraw_blinded_handle = NULL; 520 return ec; 521 } 522 } 523 else 524 { /* age restricted case */ 525 struct TALER_EXCHANGE_WithdrawBlindedAgeRestrictedCoinInput 526 ari[wh->num_coins]; 527 528 memset (ari, 529 0, 530 sizeof(ari)); 531 532 /* Prepare the blinded planchets as input */ 533 for (size_t n = 0; n < wh->num_coins; n++) 534 { 535 ari[n].denom_pub = &wh->coin_data[n].denom_pub; 536 for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) 537 ari[n].planchet_details[k] = 538 wh->coin_data[n].planchet_details[k]; 539 } 540 541 wh->withdraw_blinded_handle = 542 TALER_EXCHANGE_post_withdraw_blinded_create ( 543 wh->curl_ctx, 544 wh->keys, 545 wh->exchange_url, 546 wh->reserve_priv, 547 wh->has_blinding_seed ? &wh->blinding_seed : NULL, 548 wh->num_coins, 549 NULL); 550 if (NULL == wh->withdraw_blinded_handle) 551 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 552 TALER_EXCHANGE_post_withdraw_blinded_set_options ( 553 wh->withdraw_blinded_handle, 554 TALER_EXCHANGE_post_withdraw_blinded_option_with_age_proof ( 555 wh->max_age, 556 ari)); 557 ec = TALER_EXCHANGE_post_withdraw_blinded_start ( 558 wh->withdraw_blinded_handle, 559 ©_results_with_age_proof, 560 wh); 561 if (TALER_EC_NONE != ec) 562 { 563 TALER_EXCHANGE_post_withdraw_blinded_cancel (wh->withdraw_blinded_handle); 564 wh->withdraw_blinded_handle = NULL; 565 return ec; 566 } 567 } 568 return TALER_EC_NONE; 569 } 570 571 572 /** 573 * @brief Function called when /blinding-prepare is finished. 574 * 575 * @param cls the `struct TALER_EXCHANGE_PostWithdrawHandle *` 576 * @param bpr replies from the /blinding-prepare request 577 */ 578 static void 579 blinding_prepare_done ( 580 void *cls, 581 const struct TALER_EXCHANGE_PostBlindingPrepareResponse *bpr) 582 { 583 struct TALER_EXCHANGE_PostWithdrawHandle *wh = cls; 584 585 wh->blinding_prepare_handle = NULL; 586 switch (bpr->hr.http_status) 587 { 588 case MHD_HTTP_OK: 589 { 590 bool success = false; 591 size_t num = bpr->details.ok.num_blinding_values; 592 593 GNUNET_assert (0 != num); 594 GNUNET_assert (num == wh->num_bp_nonces); 595 for (size_t i = 0; i < wh->num_bp_coins; i++) 596 { 597 struct TALER_PlanchetDetail *planchet = wh->bp_coins[i].planchet; 598 struct CoinCandidate *can = wh->bp_coins[i].candidate; 599 size_t cs_idx = wh->bp_coins[i].cs_idx; 600 601 GNUNET_assert (NULL != can); 602 GNUNET_assert (NULL != planchet); 603 success = false; 604 605 /* Complete the initialization of the coin with CS denomination */ 606 TALER_denom_ewv_copy ( 607 &can->details.blinding_values, 608 &bpr->details.ok.blinding_values[cs_idx]); 609 610 GNUNET_assert (GNUNET_CRYPTO_BSA_CS == 611 can->details.blinding_values.blinding_inputs->cipher); 612 613 TALER_planchet_setup_coin_priv ( 614 &can->details.secret, 615 &can->details.blinding_values, 616 &can->details.coin_priv); 617 618 TALER_planchet_blinding_secret_create ( 619 &can->details.secret, 620 &can->details.blinding_values, 621 &can->details.blinding_key); 622 623 /* This initializes the 2nd half of the 624 can->planchet_detail.blinded_planchet */ 625 if (GNUNET_OK != 626 TALER_planchet_prepare ( 627 wh->bp_coins[i].denom_pub, 628 &can->details.blinding_values, 629 &can->details.blinding_key, 630 &wh->bp_nonces[cs_idx], 631 &can->details.coin_priv, 632 &can->details.h_age_commitment, 633 &can->details.h_coin_pub, 634 planchet)) 635 { 636 GNUNET_break (0); 637 break; 638 } 639 640 TALER_coin_ev_hash (&planchet->blinded_planchet, 641 &planchet->denom_pub_hash, 642 &can->blinded_coin_h); 643 success = true; 644 } 645 646 /* /blinding-prepare is done, we can now perform the 647 * actual withdraw operation */ 648 if (success) 649 { 650 enum TALER_ErrorCode ec = call_withdraw_blinded (wh); 651 652 if (TALER_EC_NONE != ec) 653 { 654 struct TALER_EXCHANGE_PostWithdrawResponse resp = { 655 .hr.ec = ec, 656 .hr.http_status = 0, 657 }; 658 659 wh->callback ( 660 wh->callback_cls, 661 &resp); 662 wh->callback = NULL; 663 TALER_EXCHANGE_post_withdraw_cancel (wh); 664 } 665 return; 666 } 667 else 668 { 669 /* prepare completed but coin setup failed */ 670 struct TALER_EXCHANGE_PostWithdrawResponse resp = { 671 .hr.ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 672 .hr.http_status = 0, 673 }; 674 675 wh->callback ( 676 wh->callback_cls, 677 &resp); 678 wh->callback = NULL; 679 TALER_EXCHANGE_post_withdraw_cancel (wh); 680 return; 681 } 682 } 683 default: 684 { 685 /* We got an error condition during blinding prepare that we need to report */ 686 struct TALER_EXCHANGE_PostWithdrawResponse resp = { 687 .hr = bpr->hr 688 }; 689 690 wh->callback ( 691 wh->callback_cls, 692 &resp); 693 wh->callback = NULL; 694 break; 695 } 696 } 697 TALER_EXCHANGE_post_withdraw_cancel (wh); 698 } 699 700 701 /** 702 * @brief Prepares coins for the call to withdraw: 703 * Performs synchronous crypto for RSA denominations, and stores 704 * the data needed for the async /blinding-prepare step for CS denominations. 705 * Does NOT start any async operations. 706 * 707 * @param wh The handler to the withdraw 708 * @param num_coins Number of coins to withdraw 709 * @param max_age The maximum age to commit to 710 * @param denoms_pub Array @e num_coins of denominations 711 * @param seed master seed from which to derive @e num_coins secrets 712 * @param blinding_seed master seed for the blinding. Might be NULL, in which 713 * case the blinding_seed is derived from @e seed 714 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure 715 */ 716 static enum GNUNET_GenericReturnValue 717 prepare_coins ( 718 struct TALER_EXCHANGE_PostWithdrawHandle *wh, 719 size_t num_coins, 720 uint8_t max_age, 721 const struct TALER_EXCHANGE_DenomPublicKey *denoms_pub, 722 const struct TALER_WithdrawMasterSeedP *seed, 723 const struct TALER_BlindingMasterSeedP *blinding_seed) 724 { 725 size_t cs_num = 0; 726 uint8_t kappa; 727 728 #define FAIL_IF(cond) \ 729 do \ 730 { \ 731 if ((cond)) \ 732 { \ 733 GNUNET_break (! (cond)); \ 734 goto ERROR; \ 735 } \ 736 } while (0) 737 738 GNUNET_assert (0 < num_coins); 739 740 wh->num_coins = num_coins; 741 wh->max_age = max_age; 742 wh->age_mask = denoms_pub[0].key.age_mask; 743 wh->coin_data = GNUNET_new_array ( 744 wh->num_coins, 745 struct CoinData); 746 747 /* First, figure out how many Clause-Schnorr denominations we have */ 748 for (size_t i =0; i< wh->num_coins; i++) 749 { 750 if (GNUNET_CRYPTO_BSA_CS == 751 denoms_pub[i].key.bsign_pub_key->cipher) 752 cs_num++; 753 } 754 755 if (wh->with_age_proof) 756 kappa = TALER_CNC_KAPPA; 757 else 758 kappa = 1; 759 760 { 761 struct TALER_PlanchetMasterSecretP secrets[kappa][num_coins]; 762 struct TALER_EXCHANGE_NonceKey cs_nonce_keys[GNUNET_NZL (cs_num)]; 763 uint32_t cs_indices[GNUNET_NZL (cs_num)]; 764 765 size_t cs_denom_idx = 0; 766 size_t cs_coin_idx = 0; 767 768 if (wh->with_age_proof) 769 { 770 TALER_withdraw_expand_kappa_seed (seed, 771 &wh->kappa_seed); 772 for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) 773 { 774 TALER_withdraw_expand_secrets ( 775 num_coins, 776 &wh->kappa_seed.tuple[k], 777 secrets[k]); 778 } 779 } 780 else 781 { 782 TALER_withdraw_expand_secrets ( 783 num_coins, 784 seed, 785 secrets[0]); 786 } 787 788 if (0 < cs_num) 789 { 790 memset (cs_nonce_keys, 791 0, 792 sizeof(cs_nonce_keys)); 793 wh->num_bp_coins = cs_num * kappa; 794 GNUNET_assert ((1 == kappa) || (cs_num * kappa > cs_num)); 795 wh->bp_coins = 796 GNUNET_new_array (wh->num_bp_coins, 797 struct BlindingPrepareCoinData); 798 wh->num_bp_nonces = cs_num; 799 wh->bp_nonces = 800 GNUNET_new_array (wh->num_bp_nonces, 801 union GNUNET_CRYPTO_BlindSessionNonce); 802 wh->num_bp_nonce_keys = cs_num; 803 wh->bp_nonce_keys = 804 GNUNET_new_array (wh->num_bp_nonce_keys, 805 struct TALER_EXCHANGE_NonceKey); 806 } 807 808 for (uint32_t i = 0; i < wh->num_coins; i++) 809 { 810 struct CoinData *cd = &wh->coin_data[i]; 811 bool age_denom = (0 != denoms_pub[i].key.age_mask.bits); 812 813 cd->denom_pub = denoms_pub[i]; 814 /* The age mask must be the same for all coins */ 815 FAIL_IF (wh->with_age_proof && 816 (0 == denoms_pub[i].key.age_mask.bits)); 817 FAIL_IF (wh->age_mask.bits != 818 denoms_pub[i].key.age_mask.bits); 819 TALER_denom_pub_copy (&cd->denom_pub.key, 820 &denoms_pub[i].key); 821 822 /* Mark the indices of the coins which are of type Clause-Schnorr 823 * and add their denomination public key hash to the list. 824 */ 825 if (GNUNET_CRYPTO_BSA_CS == 826 cd->denom_pub.key.bsign_pub_key->cipher) 827 { 828 GNUNET_assert (cs_denom_idx < cs_num); 829 cs_indices[cs_denom_idx] = i; 830 cs_nonce_keys[cs_denom_idx].cnc_num = i; 831 cs_nonce_keys[cs_denom_idx].pk = &cd->denom_pub; 832 wh->bp_nonce_keys[cs_denom_idx].cnc_num = i; 833 wh->bp_nonce_keys[cs_denom_idx].pk = &cd->denom_pub; 834 cs_denom_idx++; 835 } 836 837 /* 838 * Note that we "loop" here either only once (if with_age_proof is false), 839 * or TALER_CNC_KAPPA times. 840 */ 841 for (uint8_t k = 0; k < kappa; k++) 842 { 843 struct CoinCandidate *can = &cd->candidates[k]; 844 struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k]; 845 846 can->details.secret = secrets[k][i]; 847 /* 848 * The age restriction needs to be set on a coin if the denomination 849 * support age restriction. Note that this is regardless of whether 850 * with_age_proof is set or not. 851 */ 852 if (age_denom) 853 { 854 /* Derive the age restriction from the given secret and 855 * the maximum age */ 856 TALER_age_restriction_from_secret ( 857 &can->details.secret, 858 &wh->age_mask, 859 wh->max_age, 860 &can->details.age_commitment_proof); 861 862 TALER_age_commitment_hash ( 863 &can->details.age_commitment_proof.commitment, 864 &can->details.h_age_commitment); 865 } 866 867 switch (cd->denom_pub.key.bsign_pub_key->cipher) 868 { 869 case GNUNET_CRYPTO_BSA_RSA: 870 TALER_denom_ewv_copy (&can->details.blinding_values, 871 TALER_denom_ewv_rsa_singleton ()); 872 TALER_planchet_setup_coin_priv (&can->details.secret, 873 &can->details.blinding_values, 874 &can->details.coin_priv); 875 TALER_planchet_blinding_secret_create (&can->details.secret, 876 &can->details.blinding_values, 877 &can->details.blinding_key); 878 FAIL_IF (GNUNET_OK != 879 TALER_planchet_prepare (&cd->denom_pub.key, 880 &can->details.blinding_values, 881 &can->details.blinding_key, 882 NULL, 883 &can->details.coin_priv, 884 (age_denom) 885 ? &can->details.h_age_commitment 886 : NULL, 887 &can->details.h_coin_pub, 888 planchet)); 889 TALER_coin_ev_hash (&planchet->blinded_planchet, 890 &planchet->denom_pub_hash, 891 &can->blinded_coin_h); 892 break; 893 894 case GNUNET_CRYPTO_BSA_CS: 895 { 896 /* Prepare the nonce and save the index and the denomination for 897 * the callback after the call to blinding-prepare */ 898 wh->bp_coins[cs_coin_idx].candidate = can; 899 wh->bp_coins[cs_coin_idx].planchet = planchet; 900 wh->bp_coins[cs_coin_idx].denom_pub = &cd->denom_pub.key; 901 wh->bp_coins[cs_coin_idx].cs_idx = i; 902 wh->bp_coins[cs_coin_idx].age_denom = age_denom; 903 cs_coin_idx++; 904 break; 905 } 906 default: 907 FAIL_IF (1); 908 } 909 } 910 } 911 912 if (0 < cs_num) 913 { 914 if (wh->options.has_blinding_seed) 915 { 916 wh->blinding_seed = wh->options.blinding_seed; 917 } 918 else 919 { 920 TALER_cs_withdraw_seed_to_blinding_seed ( 921 seed, 922 &wh->blinding_seed); 923 } 924 wh->has_blinding_seed = true; 925 926 TALER_cs_derive_only_cs_blind_nonces_from_seed ( 927 &wh->blinding_seed, 928 false, /* not for melt */ 929 cs_num, 930 cs_indices, 931 wh->bp_nonces); 932 } 933 } 934 return GNUNET_OK; 935 936 ERROR: 937 if (0 < cs_num) 938 { 939 GNUNET_free (wh->bp_nonces); 940 GNUNET_free (wh->bp_coins); 941 GNUNET_free (wh->bp_nonce_keys); 942 wh->num_bp_coins = 0; 943 wh->num_bp_nonces = 0; 944 wh->num_bp_nonce_keys = 0; 945 } 946 return GNUNET_SYSERR; 947 #undef FAIL_IF 948 } 949 950 951 struct TALER_EXCHANGE_PostWithdrawHandle * 952 TALER_EXCHANGE_post_withdraw_create ( 953 struct GNUNET_CURL_Context *curl_ctx, 954 const char *exchange_url, 955 struct TALER_EXCHANGE_Keys *keys, 956 const struct TALER_ReservePrivateKeyP *reserve_priv, 957 size_t num_coins, 958 const struct TALER_EXCHANGE_DenomPublicKey denoms_pub[static num_coins], 959 const struct TALER_WithdrawMasterSeedP *seed, 960 uint8_t opaque_max_age) 961 { 962 struct TALER_EXCHANGE_PostWithdrawHandle *wh; 963 964 wh = GNUNET_new (struct TALER_EXCHANGE_PostWithdrawHandle); 965 wh->exchange_url = exchange_url; 966 wh->keys = TALER_EXCHANGE_keys_incref (keys); 967 wh->curl_ctx = curl_ctx; 968 wh->reserve_priv = reserve_priv; 969 wh->seed = *seed; 970 wh->max_age = opaque_max_age; 971 wh->init_num_coins = num_coins; 972 wh->init_denoms_pub = GNUNET_new_array (num_coins, 973 struct TALER_EXCHANGE_DenomPublicKey); 974 for (size_t i = 0; i < num_coins; i++) 975 { 976 wh->init_denoms_pub[i] = denoms_pub[i]; 977 TALER_denom_pub_copy (&wh->init_denoms_pub[i].key, 978 &denoms_pub[i].key); 979 } 980 981 return wh; 982 } 983 984 985 enum GNUNET_GenericReturnValue 986 TALER_EXCHANGE_post_withdraw_set_options_ ( 987 struct TALER_EXCHANGE_PostWithdrawHandle *pwh, 988 unsigned int num_options, 989 const struct TALER_EXCHANGE_PostWithdrawOptionValue options[]) 990 { 991 for (unsigned int i = 0; i < num_options; i++) 992 { 993 const struct TALER_EXCHANGE_PostWithdrawOptionValue *opt = &options[i]; 994 switch (opt->option) 995 { 996 case TALER_EXCHANGE_POST_WITHDRAW_OPTION_END: 997 return GNUNET_OK; 998 case TALER_EXCHANGE_POST_WITHDRAW_OPTION_WITH_AGE_PROOF: 999 pwh->with_age_proof = true; 1000 pwh->max_age = opt->details.max_age; 1001 break; 1002 case TALER_EXCHANGE_POST_WITHDRAW_OPTION_BLINDING_SEED: 1003 pwh->options.has_blinding_seed = true; 1004 pwh->options.blinding_seed = opt->details.blinding_seed; 1005 break; 1006 } 1007 } 1008 return GNUNET_OK; 1009 } 1010 1011 1012 enum TALER_ErrorCode 1013 TALER_EXCHANGE_post_withdraw_start ( 1014 struct TALER_EXCHANGE_PostWithdrawHandle *pwh, 1015 TALER_EXCHANGE_PostWithdrawCallback cb, 1016 TALER_EXCHANGE_POST_WITHDRAW_RESULT_CLOSURE *cb_cls) 1017 { 1018 pwh->callback = cb; 1019 pwh->callback_cls = cb_cls; 1020 1021 /* Run prepare_coins now that options have been applied */ 1022 if (GNUNET_OK != 1023 prepare_coins (pwh, 1024 pwh->init_num_coins, 1025 pwh->max_age, 1026 pwh->init_denoms_pub, 1027 &pwh->seed, 1028 pwh->has_blinding_seed 1029 ? &pwh->blinding_seed 1030 : NULL)) 1031 { 1032 GNUNET_free (pwh->coin_data); 1033 for (size_t i = 0; i < pwh->init_num_coins; i++) 1034 TALER_denom_pub_free (&pwh->init_denoms_pub[i].key); 1035 GNUNET_free (pwh->init_denoms_pub); 1036 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 1037 } 1038 /* Free init data - no longer needed after prepare_coins */ 1039 for (size_t i = 0; i < pwh->init_num_coins; i++) 1040 TALER_denom_pub_free (&pwh->init_denoms_pub[i].key); 1041 GNUNET_free (pwh->init_denoms_pub); 1042 1043 if (0 < pwh->num_bp_coins) 1044 { 1045 /* There are CS denominations; start the blinding-prepare request */ 1046 pwh->blinding_prepare_handle = 1047 TALER_EXCHANGE_post_blinding_prepare_for_withdraw_create ( 1048 pwh->curl_ctx, 1049 pwh->exchange_url, 1050 &pwh->blinding_seed, 1051 pwh->num_bp_nonce_keys, 1052 pwh->bp_nonce_keys); 1053 if (NULL == pwh->blinding_prepare_handle) 1054 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 1055 { 1056 enum TALER_ErrorCode ec = 1057 TALER_EXCHANGE_post_blinding_prepare_start ( 1058 pwh->blinding_prepare_handle, 1059 &blinding_prepare_done, 1060 pwh); 1061 if (TALER_EC_NONE != ec) 1062 { 1063 pwh->blinding_prepare_handle = NULL; 1064 return ec; 1065 } 1066 } 1067 return TALER_EC_NONE; 1068 } 1069 1070 /* No CS denominations; proceed directly to the withdraw protocol */ 1071 return call_withdraw_blinded (pwh); 1072 } 1073 1074 1075 void 1076 TALER_EXCHANGE_post_withdraw_cancel ( 1077 struct TALER_EXCHANGE_PostWithdrawHandle *wh) 1078 { 1079 uint8_t kappa = wh->with_age_proof ? TALER_CNC_KAPPA : 1; 1080 1081 /* Cleanup init data if _start was never called (or failed) */ 1082 if (NULL != wh->init_denoms_pub) 1083 { 1084 for (size_t i = 0; i < wh->init_num_coins; i++) 1085 TALER_denom_pub_free (&wh->init_denoms_pub[i].key); 1086 GNUNET_free (wh->init_denoms_pub); 1087 } 1088 /* Cleanup coin data */ 1089 if (NULL != wh->coin_data) 1090 { 1091 for (unsigned int i = 0; i < wh->num_coins; i++) 1092 { 1093 struct CoinData *cd = &wh->coin_data[i]; 1094 1095 for (uint8_t k = 0; k < kappa; k++) 1096 { 1097 struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k]; 1098 struct CoinCandidate *can = &cd->candidates[k]; 1099 1100 TALER_blinded_planchet_free (&planchet->blinded_planchet); 1101 TALER_denom_ewv_free (&can->details.blinding_values); 1102 TALER_age_commitment_proof_free (&can->details.age_commitment_proof); 1103 } 1104 TALER_denom_pub_free (&cd->denom_pub.key); 1105 } 1106 } 1107 1108 TALER_EXCHANGE_post_blinding_prepare_cancel (wh->blinding_prepare_handle); 1109 wh->blinding_prepare_handle = NULL; 1110 TALER_EXCHANGE_post_withdraw_blinded_cancel (wh->withdraw_blinded_handle); 1111 wh->withdraw_blinded_handle = NULL; 1112 1113 GNUNET_free (wh->bp_coins); 1114 GNUNET_free (wh->bp_nonces); 1115 GNUNET_free (wh->bp_nonce_keys); 1116 GNUNET_free (wh->coin_data); 1117 TALER_EXCHANGE_keys_decref (wh->keys); 1118 GNUNET_free (wh); 1119 } 1120 1121 1122 /* exchange_api_post-withdraw.c */