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