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