exchange_api_post-withdraw.c (32594B)
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 uint8_t k = wbr->details.created.noreveal_index; 422 struct TALER_EXCHANGE_WithdrawCoinPrivateDetails details[wh->num_coins]; 423 struct TALER_EXCHANGE_PostWithdrawResponse resp = { 424 .hr = wbr->hr, 425 }; 426 427 wh->withdraw_blinded_handle = NULL; 428 switch (wbr->hr.http_status) 429 { 430 case MHD_HTTP_OK: 431 /* in the age-restricted case, this should not happen */ 432 GNUNET_break_op (0); 433 break; 434 case MHD_HTTP_CREATED: 435 { 436 GNUNET_assert (wh->num_coins == wbr->details.created.num_coins); 437 resp.details.created = wbr->details.created; 438 resp.details.created.coin_details = details; 439 resp.details.created.kappa_seed = wh->kappa_seed; 440 memset (details, 441 0, 442 sizeof(details)); 443 for (size_t n = 0; n< wh->num_coins; n++) 444 { 445 details[n] = wh->coin_data[n].candidates[k].details; 446 details[n].planchet = wh->coin_data[n].planchet_details[k]; 447 } 448 break; 449 } 450 case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 451 resp.details.unavailable_for_legal_reasons = 452 wbr->details.unavailable_for_legal_reasons; 453 break; 454 default: 455 break; 456 } 457 458 wh->callback ( 459 wh->callback_cls, 460 &resp); 461 wh->callback = NULL; 462 TALER_EXCHANGE_post_withdraw_cancel (wh); 463 } 464 465 466 /** 467 * @brief Prepares and starts the actual TALER_EXCHANGE_post_withdraw_blinded 468 * operation once all blinding-prepare steps are done (or immediately if 469 * there are no CS denominations). 470 * 471 * @param wh The withdraw handle 472 * @return #TALER_EC_NONE on success, error code on failure 473 */ 474 static enum TALER_ErrorCode 475 call_withdraw_blinded ( 476 struct TALER_EXCHANGE_PostWithdrawHandle *wh) 477 { 478 enum TALER_ErrorCode ec; 479 480 GNUNET_assert (NULL == wh->blinding_prepare_handle); 481 482 if (! wh->with_age_proof) 483 { 484 struct TALER_EXCHANGE_WithdrawBlindedCoinInput input[wh->num_coins]; 485 486 memset (input, 487 0, 488 sizeof(input)); 489 490 /* Prepare the blinded planchets as input */ 491 for (size_t n = 0; n < wh->num_coins; n++) 492 { 493 input[n].denom_pub = 494 &wh->coin_data[n].denom_pub; 495 input[n].planchet_details = 496 *wh->coin_data[n].planchet_details; 497 } 498 499 wh->withdraw_blinded_handle = 500 TALER_EXCHANGE_post_withdraw_blinded_create ( 501 wh->curl_ctx, 502 wh->keys, 503 wh->exchange_url, 504 wh->reserve_priv, 505 wh->has_blinding_seed ? &wh->blinding_seed : NULL, 506 wh->num_coins, 507 input); 508 if (NULL == wh->withdraw_blinded_handle) 509 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 510 ec = TALER_EXCHANGE_post_withdraw_blinded_start ( 511 wh->withdraw_blinded_handle, 512 ©_results, 513 wh); 514 if (TALER_EC_NONE != ec) 515 { 516 wh->withdraw_blinded_handle = NULL; 517 return ec; 518 } 519 } 520 else 521 { /* age restricted case */ 522 struct TALER_EXCHANGE_WithdrawBlindedAgeRestrictedCoinInput 523 ari[wh->num_coins]; 524 525 memset (ari, 526 0, 527 sizeof(ari)); 528 529 /* Prepare the blinded planchets as input */ 530 for (size_t n = 0; n < wh->num_coins; n++) 531 { 532 ari[n].denom_pub = &wh->coin_data[n].denom_pub; 533 for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) 534 ari[n].planchet_details[k] = 535 wh->coin_data[n].planchet_details[k]; 536 } 537 538 wh->withdraw_blinded_handle = 539 TALER_EXCHANGE_post_withdraw_blinded_create ( 540 wh->curl_ctx, 541 wh->keys, 542 wh->exchange_url, 543 wh->reserve_priv, 544 wh->has_blinding_seed ? &wh->blinding_seed : NULL, 545 wh->num_coins, 546 NULL); 547 if (NULL == wh->withdraw_blinded_handle) 548 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 549 TALER_EXCHANGE_post_withdraw_blinded_set_options ( 550 wh->withdraw_blinded_handle, 551 TALER_EXCHANGE_post_withdraw_blinded_option_with_age_proof ( 552 wh->max_age, 553 ari)); 554 ec = TALER_EXCHANGE_post_withdraw_blinded_start ( 555 wh->withdraw_blinded_handle, 556 ©_results_with_age_proof, 557 wh); 558 if (TALER_EC_NONE != ec) 559 { 560 TALER_EXCHANGE_post_withdraw_blinded_cancel (wh->withdraw_blinded_handle); 561 wh->withdraw_blinded_handle = NULL; 562 return ec; 563 } 564 } 565 return TALER_EC_NONE; 566 } 567 568 569 /** 570 * @brief Function called when /blinding-prepare is finished. 571 * 572 * @param cls the `struct TALER_EXCHANGE_PostWithdrawHandle *` 573 * @param bpr replies from the /blinding-prepare request 574 */ 575 static void 576 blinding_prepare_done ( 577 void *cls, 578 const struct TALER_EXCHANGE_PostBlindingPrepareResponse *bpr) 579 { 580 struct TALER_EXCHANGE_PostWithdrawHandle *wh = cls; 581 582 wh->blinding_prepare_handle = NULL; 583 switch (bpr->hr.http_status) 584 { 585 case MHD_HTTP_OK: 586 { 587 bool success = false; 588 size_t num = bpr->details.ok.num_blinding_values; 589 590 GNUNET_assert (0 != num); 591 GNUNET_assert (num == wh->num_bp_nonces); 592 for (size_t i = 0; i < wh->num_bp_coins; i++) 593 { 594 struct TALER_PlanchetDetail *planchet = wh->bp_coins[i].planchet; 595 struct CoinCandidate *can = wh->bp_coins[i].candidate; 596 size_t cs_idx = wh->bp_coins[i].cs_idx; 597 598 GNUNET_assert (NULL != can); 599 GNUNET_assert (NULL != planchet); 600 success = false; 601 602 /* Complete the initialization of the coin with CS denomination */ 603 TALER_denom_ewv_copy ( 604 &can->details.blinding_values, 605 &bpr->details.ok.blinding_values[cs_idx]); 606 607 GNUNET_assert (GNUNET_CRYPTO_BSA_CS == 608 can->details.blinding_values.blinding_inputs->cipher); 609 610 TALER_planchet_setup_coin_priv ( 611 &can->details.secret, 612 &can->details.blinding_values, 613 &can->details.coin_priv); 614 615 TALER_planchet_blinding_secret_create ( 616 &can->details.secret, 617 &can->details.blinding_values, 618 &can->details.blinding_key); 619 620 /* This initializes the 2nd half of the 621 can->planchet_detail.blinded_planchet */ 622 if (GNUNET_OK != 623 TALER_planchet_prepare ( 624 wh->bp_coins[i].denom_pub, 625 &can->details.blinding_values, 626 &can->details.blinding_key, 627 &wh->bp_nonces[cs_idx], 628 &can->details.coin_priv, 629 &can->details.h_age_commitment, 630 &can->details.h_coin_pub, 631 planchet)) 632 { 633 GNUNET_break (0); 634 break; 635 } 636 637 TALER_coin_ev_hash (&planchet->blinded_planchet, 638 &planchet->denom_pub_hash, 639 &can->blinded_coin_h); 640 success = true; 641 } 642 643 /* /blinding-prepare is done, we can now perform the 644 * actual withdraw operation */ 645 if (success) 646 { 647 enum TALER_ErrorCode ec = call_withdraw_blinded (wh); 648 649 if (TALER_EC_NONE != ec) 650 { 651 struct TALER_EXCHANGE_PostWithdrawResponse resp = { 652 .hr.ec = ec, 653 .hr.http_status = 0, 654 }; 655 656 wh->callback ( 657 wh->callback_cls, 658 &resp); 659 wh->callback = NULL; 660 TALER_EXCHANGE_post_withdraw_cancel (wh); 661 } 662 return; 663 } 664 else 665 { 666 /* prepare completed but coin setup failed */ 667 struct TALER_EXCHANGE_PostWithdrawResponse resp = { 668 .hr.ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 669 .hr.http_status = 0, 670 }; 671 672 wh->callback ( 673 wh->callback_cls, 674 &resp); 675 wh->callback = NULL; 676 TALER_EXCHANGE_post_withdraw_cancel (wh); 677 return; 678 } 679 } 680 default: 681 { 682 /* We got an error condition during blinding prepare that we need to report */ 683 struct TALER_EXCHANGE_PostWithdrawResponse resp = { 684 .hr = bpr->hr 685 }; 686 687 wh->callback ( 688 wh->callback_cls, 689 &resp); 690 wh->callback = NULL; 691 break; 692 } 693 } 694 TALER_EXCHANGE_post_withdraw_cancel (wh); 695 } 696 697 698 /** 699 * @brief Prepares coins for the call to withdraw: 700 * Performs synchronous crypto for RSA denominations, and stores 701 * the data needed for the async /blinding-prepare step for CS denominations. 702 * Does NOT start any async operations. 703 * 704 * @param wh The handler to the withdraw 705 * @param num_coins Number of coins to withdraw 706 * @param max_age The maximum age to commit to 707 * @param denoms_pub Array @e num_coins of denominations 708 * @param seed master seed from which to derive @e num_coins secrets 709 * @param blinding_seed master seed for the blinding. Might be NULL, in which 710 * case the blinding_seed is derived from @e seed 711 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure 712 */ 713 static enum GNUNET_GenericReturnValue 714 prepare_coins ( 715 struct TALER_EXCHANGE_PostWithdrawHandle *wh, 716 size_t num_coins, 717 uint8_t max_age, 718 const struct TALER_EXCHANGE_DenomPublicKey *denoms_pub, 719 const struct TALER_WithdrawMasterSeedP *seed, 720 const struct TALER_BlindingMasterSeedP *blinding_seed) 721 { 722 size_t cs_num = 0; 723 uint8_t kappa; 724 725 #define FAIL_IF(cond) \ 726 do \ 727 { \ 728 if ((cond)) \ 729 { \ 730 GNUNET_break (! (cond)); \ 731 goto ERROR; \ 732 } \ 733 } while (0) 734 735 GNUNET_assert (0 < num_coins); 736 737 wh->num_coins = num_coins; 738 wh->max_age = max_age; 739 wh->age_mask = denoms_pub[0].key.age_mask; 740 wh->coin_data = GNUNET_new_array ( 741 wh->num_coins, 742 struct CoinData); 743 744 /* First, figure out how many Clause-Schnorr denominations we have */ 745 for (size_t i =0; i< wh->num_coins; i++) 746 { 747 if (GNUNET_CRYPTO_BSA_CS == 748 denoms_pub[i].key.bsign_pub_key->cipher) 749 cs_num++; 750 } 751 752 if (wh->with_age_proof) 753 kappa = TALER_CNC_KAPPA; 754 else 755 kappa = 1; 756 757 { 758 struct TALER_PlanchetMasterSecretP secrets[kappa][num_coins]; 759 struct TALER_EXCHANGE_NonceKey cs_nonce_keys[GNUNET_NZL (cs_num)]; 760 uint32_t cs_indices[GNUNET_NZL (cs_num)]; 761 762 size_t cs_denom_idx = 0; 763 size_t cs_coin_idx = 0; 764 765 if (wh->with_age_proof) 766 { 767 TALER_withdraw_expand_kappa_seed (seed, 768 &wh->kappa_seed); 769 for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) 770 { 771 TALER_withdraw_expand_secrets ( 772 num_coins, 773 &wh->kappa_seed.tuple[k], 774 secrets[k]); 775 } 776 } 777 else 778 { 779 TALER_withdraw_expand_secrets ( 780 num_coins, 781 seed, 782 secrets[0]); 783 } 784 785 if (0 < cs_num) 786 { 787 memset (cs_nonce_keys, 788 0, 789 sizeof(cs_nonce_keys)); 790 wh->num_bp_coins = cs_num * kappa; 791 GNUNET_assert ((1 == kappa) || (cs_num * kappa > cs_num)); 792 wh->bp_coins = 793 GNUNET_new_array (wh->num_bp_coins, 794 struct BlindingPrepareCoinData); 795 wh->num_bp_nonces = cs_num; 796 wh->bp_nonces = 797 GNUNET_new_array (wh->num_bp_nonces, 798 union GNUNET_CRYPTO_BlindSessionNonce); 799 wh->num_bp_nonce_keys = cs_num; 800 wh->bp_nonce_keys = 801 GNUNET_new_array (wh->num_bp_nonce_keys, 802 struct TALER_EXCHANGE_NonceKey); 803 } 804 805 for (uint32_t i = 0; i < wh->num_coins; i++) 806 { 807 struct CoinData *cd = &wh->coin_data[i]; 808 bool age_denom = (0 != denoms_pub[i].key.age_mask.bits); 809 810 cd->denom_pub = denoms_pub[i]; 811 /* The age mask must be the same for all coins */ 812 FAIL_IF (wh->with_age_proof && 813 (0 == denoms_pub[i].key.age_mask.bits)); 814 FAIL_IF (wh->age_mask.bits != 815 denoms_pub[i].key.age_mask.bits); 816 TALER_denom_pub_copy (&cd->denom_pub.key, 817 &denoms_pub[i].key); 818 819 /* Mark the indices of the coins which are of type Clause-Schnorr 820 * and add their denomination public key hash to the list. 821 */ 822 if (GNUNET_CRYPTO_BSA_CS == 823 cd->denom_pub.key.bsign_pub_key->cipher) 824 { 825 GNUNET_assert (cs_denom_idx < cs_num); 826 cs_indices[cs_denom_idx] = i; 827 cs_nonce_keys[cs_denom_idx].cnc_num = i; 828 cs_nonce_keys[cs_denom_idx].pk = &cd->denom_pub; 829 wh->bp_nonce_keys[cs_denom_idx].cnc_num = i; 830 wh->bp_nonce_keys[cs_denom_idx].pk = &cd->denom_pub; 831 cs_denom_idx++; 832 } 833 834 /* 835 * Note that we "loop" here either only once (if with_age_proof is false), 836 * or TALER_CNC_KAPPA times. 837 */ 838 for (uint8_t k = 0; k < kappa; k++) 839 { 840 struct CoinCandidate *can = &cd->candidates[k]; 841 struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k]; 842 843 can->details.secret = secrets[k][i]; 844 /* 845 * The age restriction needs to be set on a coin if the denomination 846 * support age restriction. Note that this is regardless of whether 847 * with_age_proof is set or not. 848 */ 849 if (age_denom) 850 { 851 /* Derive the age restriction from the given secret and 852 * the maximum age */ 853 TALER_age_restriction_from_secret ( 854 &can->details.secret, 855 &wh->age_mask, 856 wh->max_age, 857 &can->details.age_commitment_proof); 858 859 TALER_age_commitment_hash ( 860 &can->details.age_commitment_proof.commitment, 861 &can->details.h_age_commitment); 862 } 863 864 switch (cd->denom_pub.key.bsign_pub_key->cipher) 865 { 866 case GNUNET_CRYPTO_BSA_RSA: 867 TALER_denom_ewv_copy (&can->details.blinding_values, 868 TALER_denom_ewv_rsa_singleton ()); 869 TALER_planchet_setup_coin_priv (&can->details.secret, 870 &can->details.blinding_values, 871 &can->details.coin_priv); 872 TALER_planchet_blinding_secret_create (&can->details.secret, 873 &can->details.blinding_values, 874 &can->details.blinding_key); 875 FAIL_IF (GNUNET_OK != 876 TALER_planchet_prepare (&cd->denom_pub.key, 877 &can->details.blinding_values, 878 &can->details.blinding_key, 879 NULL, 880 &can->details.coin_priv, 881 (age_denom) 882 ? &can->details.h_age_commitment 883 : NULL, 884 &can->details.h_coin_pub, 885 planchet)); 886 TALER_coin_ev_hash (&planchet->blinded_planchet, 887 &planchet->denom_pub_hash, 888 &can->blinded_coin_h); 889 break; 890 891 case GNUNET_CRYPTO_BSA_CS: 892 { 893 /* Prepare the nonce and save the index and the denomination for 894 * the callback after the call to blinding-prepare */ 895 wh->bp_coins[cs_coin_idx].candidate = can; 896 wh->bp_coins[cs_coin_idx].planchet = planchet; 897 wh->bp_coins[cs_coin_idx].denom_pub = &cd->denom_pub.key; 898 wh->bp_coins[cs_coin_idx].cs_idx = i; 899 wh->bp_coins[cs_coin_idx].age_denom = age_denom; 900 cs_coin_idx++; 901 break; 902 } 903 default: 904 FAIL_IF (1); 905 } 906 } 907 } 908 909 if (0 < cs_num) 910 { 911 if (wh->options.has_blinding_seed) 912 { 913 wh->blinding_seed = wh->options.blinding_seed; 914 } 915 else 916 { 917 TALER_cs_withdraw_seed_to_blinding_seed ( 918 seed, 919 &wh->blinding_seed); 920 } 921 wh->has_blinding_seed = true; 922 923 TALER_cs_derive_only_cs_blind_nonces_from_seed ( 924 &wh->blinding_seed, 925 false, /* not for melt */ 926 cs_num, 927 cs_indices, 928 wh->bp_nonces); 929 } 930 } 931 return GNUNET_OK; 932 933 ERROR: 934 if (0 < cs_num) 935 { 936 GNUNET_free (wh->bp_nonces); 937 GNUNET_free (wh->bp_coins); 938 GNUNET_free (wh->bp_nonce_keys); 939 wh->num_bp_coins = 0; 940 wh->num_bp_nonces = 0; 941 wh->num_bp_nonce_keys = 0; 942 } 943 return GNUNET_SYSERR; 944 #undef FAIL_IF 945 } 946 947 948 struct TALER_EXCHANGE_PostWithdrawHandle * 949 TALER_EXCHANGE_post_withdraw_create ( 950 struct GNUNET_CURL_Context *curl_ctx, 951 const char *exchange_url, 952 struct TALER_EXCHANGE_Keys *keys, 953 const struct TALER_ReservePrivateKeyP *reserve_priv, 954 size_t num_coins, 955 const struct TALER_EXCHANGE_DenomPublicKey denoms_pub[static num_coins], 956 const struct TALER_WithdrawMasterSeedP *seed, 957 uint8_t opaque_max_age) 958 { 959 struct TALER_EXCHANGE_PostWithdrawHandle *wh; 960 961 wh = GNUNET_new (struct TALER_EXCHANGE_PostWithdrawHandle); 962 wh->exchange_url = exchange_url; 963 wh->keys = TALER_EXCHANGE_keys_incref (keys); 964 wh->curl_ctx = curl_ctx; 965 wh->reserve_priv = reserve_priv; 966 wh->seed = *seed; 967 wh->max_age = opaque_max_age; 968 wh->init_num_coins = num_coins; 969 wh->init_denoms_pub = GNUNET_new_array (num_coins, 970 struct TALER_EXCHANGE_DenomPublicKey); 971 for (size_t i = 0; i < num_coins; i++) 972 { 973 wh->init_denoms_pub[i] = denoms_pub[i]; 974 TALER_denom_pub_copy (&wh->init_denoms_pub[i].key, 975 &denoms_pub[i].key); 976 } 977 978 return wh; 979 } 980 981 982 enum GNUNET_GenericReturnValue 983 TALER_EXCHANGE_post_withdraw_set_options_ ( 984 struct TALER_EXCHANGE_PostWithdrawHandle *pwh, 985 unsigned int num_options, 986 const struct TALER_EXCHANGE_PostWithdrawOptionValue options[]) 987 { 988 for (unsigned int i = 0; i < num_options; i++) 989 { 990 const struct TALER_EXCHANGE_PostWithdrawOptionValue *opt = &options[i]; 991 switch (opt->option) 992 { 993 case TALER_EXCHANGE_POST_WITHDRAW_OPTION_END: 994 return GNUNET_OK; 995 case TALER_EXCHANGE_POST_WITHDRAW_OPTION_WITH_AGE_PROOF: 996 pwh->with_age_proof = true; 997 pwh->max_age = opt->details.max_age; 998 break; 999 case TALER_EXCHANGE_POST_WITHDRAW_OPTION_BLINDING_SEED: 1000 pwh->options.has_blinding_seed = true; 1001 pwh->options.blinding_seed = opt->details.blinding_seed; 1002 break; 1003 } 1004 } 1005 return GNUNET_OK; 1006 } 1007 1008 1009 enum TALER_ErrorCode 1010 TALER_EXCHANGE_post_withdraw_start ( 1011 struct TALER_EXCHANGE_PostWithdrawHandle *pwh, 1012 TALER_EXCHANGE_PostWithdrawCallback cb, 1013 TALER_EXCHANGE_POST_WITHDRAW_RESULT_CLOSURE *cb_cls) 1014 { 1015 pwh->callback = cb; 1016 pwh->callback_cls = cb_cls; 1017 1018 /* Run prepare_coins now that options have been applied */ 1019 if (GNUNET_OK != 1020 prepare_coins (pwh, 1021 pwh->init_num_coins, 1022 pwh->max_age, 1023 pwh->init_denoms_pub, 1024 &pwh->seed, 1025 pwh->has_blinding_seed 1026 ? &pwh->blinding_seed 1027 : NULL)) 1028 { 1029 GNUNET_free (pwh->coin_data); 1030 for (size_t i = 0; i < pwh->init_num_coins; i++) 1031 TALER_denom_pub_free (&pwh->init_denoms_pub[i].key); 1032 GNUNET_free (pwh->init_denoms_pub); 1033 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 1034 } 1035 /* Free init data - no longer needed after prepare_coins */ 1036 for (size_t i = 0; i < pwh->init_num_coins; i++) 1037 TALER_denom_pub_free (&pwh->init_denoms_pub[i].key); 1038 GNUNET_free (pwh->init_denoms_pub); 1039 1040 if (0 < pwh->num_bp_coins) 1041 { 1042 /* There are CS denominations; start the blinding-prepare request */ 1043 pwh->blinding_prepare_handle = 1044 TALER_EXCHANGE_post_blinding_prepare_for_withdraw_create ( 1045 pwh->curl_ctx, 1046 pwh->exchange_url, 1047 &pwh->blinding_seed, 1048 pwh->num_bp_nonce_keys, 1049 pwh->bp_nonce_keys); 1050 if (NULL == pwh->blinding_prepare_handle) 1051 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 1052 { 1053 enum TALER_ErrorCode ec = 1054 TALER_EXCHANGE_post_blinding_prepare_start ( 1055 pwh->blinding_prepare_handle, 1056 &blinding_prepare_done, 1057 pwh); 1058 if (TALER_EC_NONE != ec) 1059 { 1060 pwh->blinding_prepare_handle = NULL; 1061 return ec; 1062 } 1063 } 1064 return TALER_EC_NONE; 1065 } 1066 1067 /* No CS denominations; proceed directly to the withdraw protocol */ 1068 return call_withdraw_blinded (pwh); 1069 } 1070 1071 1072 void 1073 TALER_EXCHANGE_post_withdraw_cancel ( 1074 struct TALER_EXCHANGE_PostWithdrawHandle *wh) 1075 { 1076 uint8_t kappa = wh->with_age_proof ? TALER_CNC_KAPPA : 1; 1077 1078 /* Cleanup init data if _start was never called (or failed) */ 1079 if (NULL != wh->init_denoms_pub) 1080 { 1081 for (size_t i = 0; i < wh->init_num_coins; i++) 1082 TALER_denom_pub_free (&wh->init_denoms_pub[i].key); 1083 GNUNET_free (wh->init_denoms_pub); 1084 } 1085 /* Cleanup coin data */ 1086 if (NULL != wh->coin_data) 1087 { 1088 for (unsigned int i = 0; i < wh->num_coins; i++) 1089 { 1090 struct CoinData *cd = &wh->coin_data[i]; 1091 1092 for (uint8_t k = 0; k < kappa; k++) 1093 { 1094 struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k]; 1095 struct CoinCandidate *can = &cd->candidates[k]; 1096 1097 TALER_blinded_planchet_free (&planchet->blinded_planchet); 1098 TALER_denom_ewv_free (&can->details.blinding_values); 1099 TALER_age_commitment_proof_free (&can->details.age_commitment_proof); 1100 } 1101 TALER_denom_pub_free (&cd->denom_pub.key); 1102 } 1103 } 1104 1105 TALER_EXCHANGE_post_blinding_prepare_cancel (wh->blinding_prepare_handle); 1106 wh->blinding_prepare_handle = NULL; 1107 TALER_EXCHANGE_post_withdraw_blinded_cancel (wh->withdraw_blinded_handle); 1108 wh->withdraw_blinded_handle = NULL; 1109 1110 GNUNET_free (wh->bp_coins); 1111 GNUNET_free (wh->bp_nonces); 1112 GNUNET_free (wh->bp_nonce_keys); 1113 GNUNET_free (wh->coin_data); 1114 TALER_EXCHANGE_keys_decref (wh->keys); 1115 GNUNET_free (wh); 1116 } 1117 1118 1119 /* exchange_api_post-withdraw.c */