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