exchange_api_post-melt.c (19887B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2025 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-melt.c 19 * @brief Implementation of the /melt request 20 * @author Özgür Kesim 21 */ 22 #include "taler/platform.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 "taler/taler_json_lib.h" 29 #include "taler/taler_exchange_service.h" 30 #include "exchange_api_common.h" 31 #include "exchange_api_handle.h" 32 #include "taler/taler_signatures.h" 33 #include "exchange_api_curl_defaults.h" 34 #include "exchange_api_refresh_common.h" 35 36 37 /** 38 * @brief A /melt Handle 39 */ 40 struct TALER_EXCHANGE_PostMeltHandle 41 { 42 43 /** 44 * The keys of the this request handle will use 45 */ 46 struct TALER_EXCHANGE_Keys *keys; 47 48 /** 49 * The url for this request. 50 */ 51 char *url; 52 53 /** 54 * The exchange base url. 55 */ 56 char *exchange_url; 57 58 /** 59 * Curl context. 60 */ 61 struct GNUNET_CURL_Context *cctx; 62 63 /** 64 * Context for #TEH_curl_easy_post(). Keeps the data that must 65 * persist for Curl to make the upload. 66 */ 67 struct TALER_CURL_PostContext ctx; 68 69 /** 70 * Handle for the request. 71 */ 72 struct GNUNET_CURL_Job *job; 73 74 /** 75 * Function to call with refresh melt failure results. 76 */ 77 TALER_EXCHANGE_PostMeltCallback melt_cb; 78 79 /** 80 * Closure for @e result_cb and @e melt_failure_cb. 81 */ 82 void *melt_cb_cls; 83 84 /** 85 * Actual information about the melt operation. 86 */ 87 struct MeltData md; 88 89 /** 90 * The seed for the melt operation. 91 */ 92 struct TALER_PublicRefreshMasterSeedP rms; 93 94 /** 95 * Details about the characteristics of the requested melt operation. 96 */ 97 const struct TALER_EXCHANGE_MeltInput *rd; 98 99 /** 100 * True, if no blinding_seed is needed (no CS denominations involved) 101 */ 102 bool no_blinding_seed; 103 104 /** 105 * If @e no_blinding_seed is false, the blinding seed for the intermediate 106 * call to /blinding-prepare, in order to retrieve the R-values from the 107 * exchange for the blind Clause-Schnorr signature. 108 */ 109 struct TALER_BlindingMasterSeedP blinding_seed; 110 111 /** 112 * Array of `num_fresh_denom_pubs` per-coin values 113 * returned from melt operation. 114 */ 115 struct TALER_ExchangeBlindingValues *melt_blinding_values; 116 117 /** 118 * Handle for the preflight request, or NULL. 119 */ 120 struct TALER_EXCHANGE_PostBlindingPrepareHandle *bpr; 121 122 /** 123 * Public key of the coin being melted. 124 */ 125 struct TALER_CoinSpendPublicKeyP coin_pub; 126 127 /** 128 * Signature affirming the melt. 129 */ 130 struct TALER_CoinSpendSignatureP coin_sig; 131 132 /** 133 * @brief Public information about the coin's denomination key 134 */ 135 const struct TALER_EXCHANGE_DenomPublicKey *dki; 136 137 /** 138 * Gamma value chosen by the exchange during melt. 139 */ 140 uint32_t noreveal_index; 141 142 }; 143 144 145 /** 146 * Verify that the signature on the "200 OK" response 147 * from the exchange is valid. 148 * 149 * @param[in,out] mh melt handle 150 * @param json json reply with the signature 151 * @param[out] exchange_pub public key of the exchange used for the signature 152 * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not 153 */ 154 static enum GNUNET_GenericReturnValue 155 verify_melt_signature_ok (struct TALER_EXCHANGE_PostMeltHandle *mh, 156 const json_t *json, 157 struct TALER_ExchangePublicKeyP *exchange_pub) 158 { 159 struct TALER_ExchangeSignatureP exchange_sig; 160 struct GNUNET_JSON_Specification spec[] = { 161 GNUNET_JSON_spec_fixed_auto ("exchange_sig", 162 &exchange_sig), 163 GNUNET_JSON_spec_fixed_auto ("exchange_pub", 164 exchange_pub), 165 GNUNET_JSON_spec_uint32 ("noreveal_index", 166 &mh->noreveal_index), 167 GNUNET_JSON_spec_end () 168 }; 169 170 if (GNUNET_OK != 171 GNUNET_JSON_parse (json, 172 spec, 173 NULL, NULL)) 174 { 175 GNUNET_break_op (0); 176 return GNUNET_SYSERR; 177 } 178 /* check that exchange signing key is permitted */ 179 if (GNUNET_OK != 180 TALER_EXCHANGE_test_signing_key (mh->keys, 181 exchange_pub)) 182 { 183 GNUNET_break_op (0); 184 return GNUNET_SYSERR; 185 } 186 187 /* check that noreveal index is in permitted range */ 188 if (TALER_CNC_KAPPA <= mh->noreveal_index) 189 { 190 GNUNET_break_op (0); 191 return GNUNET_SYSERR; 192 } 193 194 if (GNUNET_OK != 195 TALER_exchange_online_melt_confirmation_verify ( 196 &mh->md.rc, 197 mh->noreveal_index, 198 exchange_pub, 199 &exchange_sig)) 200 { 201 GNUNET_break_op (0); 202 return GNUNET_SYSERR; 203 } 204 return GNUNET_OK; 205 } 206 207 208 /** 209 * Function called when we're done processing the 210 * HTTP /melt request. 211 * 212 * @param cls the `struct TALER_EXCHANGE_MeltHandle` 213 * @param response_code HTTP response code, 0 on error 214 * @param response parsed JSON result, NULL on error 215 */ 216 static void 217 handle_melt_finished (void *cls, 218 long response_code, 219 const void *response) 220 { 221 struct TALER_EXCHANGE_PostMeltHandle *mh = cls; 222 const json_t *j = response; 223 struct TALER_EXCHANGE_PostMeltResponse mr = { 224 .hr.reply = j, 225 .hr.http_status = (unsigned int) response_code 226 }; 227 228 mh->job = NULL; 229 switch (response_code) 230 { 231 case 0: 232 mr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 233 break; 234 case MHD_HTTP_OK: 235 if (GNUNET_OK != 236 verify_melt_signature_ok (mh, 237 j, 238 &mr.details.ok.sign_key)) 239 { 240 GNUNET_break_op (0); 241 mr.hr.http_status = 0; 242 mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE; 243 break; 244 } 245 mr.details.ok.noreveal_index = mh->noreveal_index; 246 mr.details.ok.num_melt_blinding_values = mh->rd->num_fresh_denom_pubs; 247 mr.details.ok.melt_blinding_values = mh->melt_blinding_values; 248 mr.details.ok.blinding_seed = mh->no_blinding_seed 249 ? NULL 250 : &mh->blinding_seed; 251 mh->melt_cb (mh->melt_cb_cls, 252 &mr); 253 mh->melt_cb = NULL; 254 break; 255 case MHD_HTTP_BAD_REQUEST: 256 /* This should never happen, either us or the exchange is buggy 257 (or API version conflict); just pass JSON reply to the application */ 258 mr.hr.ec = TALER_JSON_get_error_code (j); 259 mr.hr.hint = TALER_JSON_get_error_hint (j); 260 break; 261 case MHD_HTTP_CONFLICT: 262 mr.hr.ec = TALER_JSON_get_error_code (j); 263 mr.hr.hint = TALER_JSON_get_error_hint (j); 264 break; 265 case MHD_HTTP_FORBIDDEN: 266 /* Nothing really to verify, exchange says one of the signatures is 267 invalid; assuming we checked them, this should never happen, we 268 should pass the JSON reply to the application */ 269 mr.hr.ec = TALER_JSON_get_error_code (j); 270 mr.hr.hint = TALER_JSON_get_error_hint (j); 271 break; 272 case MHD_HTTP_NOT_FOUND: 273 /* Nothing really to verify, this should never 274 happen, we should pass the JSON reply to the application */ 275 mr.hr.ec = TALER_JSON_get_error_code (j); 276 mr.hr.hint = TALER_JSON_get_error_hint (j); 277 break; 278 case MHD_HTTP_INTERNAL_SERVER_ERROR: 279 /* Server had an internal issue; we should retry, but this API 280 leaves this to the application */ 281 mr.hr.ec = TALER_JSON_get_error_code (j); 282 mr.hr.hint = TALER_JSON_get_error_hint (j); 283 break; 284 default: 285 /* unexpected response code */ 286 mr.hr.ec = TALER_JSON_get_error_code (j); 287 mr.hr.hint = TALER_JSON_get_error_hint (j); 288 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 289 "Unexpected response code %u/%d for exchange melt\n", 290 (unsigned int) response_code, 291 mr.hr.ec); 292 GNUNET_break_op (0); 293 break; 294 } 295 if (NULL != mh->melt_cb) 296 mh->melt_cb (mh->melt_cb_cls, 297 &mr); 298 TALER_EXCHANGE_post_melt_cancel (mh); 299 } 300 301 302 /** 303 * Start the actual melt operation, now that we have 304 * the exchange's input values. 305 * 306 * @param[in,out] mh melt operation to run 307 * @return #GNUNET_OK if we could start the operation 308 */ 309 static enum GNUNET_GenericReturnValue 310 start_melt (struct TALER_EXCHANGE_PostMeltHandle *mh) 311 { 312 json_t *j_request_body; 313 json_t *j_transfer_pubs; 314 json_t *j_coin_evs; 315 CURL *eh; 316 struct TALER_DenominationHashP h_denom_pub; 317 318 if (GNUNET_OK != 319 TALER_EXCHANGE_get_melt_data (&mh->rms, 320 mh->rd, 321 mh->no_blinding_seed 322 ? NULL 323 : &mh->blinding_seed, 324 mh->melt_blinding_values, 325 &mh->md)) 326 { 327 GNUNET_break (0); 328 return GNUNET_SYSERR; 329 } 330 TALER_denom_pub_hash ( 331 &mh->md.melted_coin.pub_key, 332 &h_denom_pub); 333 TALER_wallet_melt_sign ( 334 &mh->md.melted_coin.melt_amount_with_fee, 335 &mh->md.melted_coin.fee_melt, 336 &mh->md.rc, 337 &h_denom_pub, 338 mh->md.melted_coin.h_age_commitment, 339 &mh->md.melted_coin.coin_priv, 340 &mh->coin_sig); 341 GNUNET_CRYPTO_eddsa_key_get_public ( 342 &mh->md.melted_coin.coin_priv.eddsa_priv, 343 &mh->coin_pub.eddsa_pub); 344 mh->dki = TALER_EXCHANGE_get_denomination_key ( 345 mh->keys, 346 &mh->md.melted_coin.pub_key); 347 j_request_body = GNUNET_JSON_PACK ( 348 GNUNET_JSON_pack_data_auto ("old_coin_pub", 349 &mh->coin_pub), 350 GNUNET_JSON_pack_data_auto ("old_denom_pub_h", 351 &h_denom_pub), 352 TALER_JSON_pack_denom_sig ("old_denom_sig", 353 &mh->md.melted_coin.sig), 354 GNUNET_JSON_pack_data_auto ("confirm_sig", 355 &mh->coin_sig), 356 TALER_JSON_pack_amount ("value_with_fee", 357 &mh->md.melted_coin.melt_amount_with_fee), 358 GNUNET_JSON_pack_allow_null ( 359 (NULL != mh->md.melted_coin.h_age_commitment) 360 ? GNUNET_JSON_pack_data_auto ("old_age_commitment_h", 361 mh->md.melted_coin.h_age_commitment) 362 : GNUNET_JSON_pack_string ("old_age_commitment_h", 363 NULL)), 364 GNUNET_JSON_pack_data_auto ("refresh_seed", 365 &mh->md.refresh_seed), 366 GNUNET_JSON_pack_allow_null ( 367 (mh->md.no_blinding_seed) 368 ? GNUNET_JSON_pack_string ("blinding_seed", 369 NULL) 370 : GNUNET_JSON_pack_data_auto ("blinding_seed", 371 &mh->md.blinding_seed)), 372 TALER_JSON_pack_array_of_data_auto ("denoms_h", 373 mh->md.num_fresh_coins, 374 mh->md.denoms_h) 375 ); 376 GNUNET_assert (NULL != j_request_body); 377 GNUNET_assert (NULL != 378 (j_transfer_pubs = json_array ())); 379 GNUNET_assert (NULL != 380 (j_coin_evs = json_array ())); 381 /** 382 * Fill the kappa array of coin envelopes and 383 * the array of transfer pubs. 384 */ 385 for (uint8_t k=0; k<TALER_CNC_KAPPA; k++) 386 { 387 json_t *j_envs; 388 json_t *j_tbs = GNUNET_JSON_PACK ( 389 TALER_JSON_pack_array_of_data_auto (NULL, 390 mh->md.num_fresh_coins, 391 mh->md.kappa_transfer_pubs[k]) 392 ); 393 394 GNUNET_assert (NULL != (j_envs = json_array ())); 395 GNUNET_assert (NULL !=j_tbs); 396 397 for (size_t i = 0; i < mh->md.num_fresh_coins; i++) 398 { 399 json_t *j_coin = GNUNET_JSON_PACK ( 400 TALER_JSON_pack_blinded_planchet (NULL, 401 &mh->md.kappa_blinded_planchets[k][i]) 402 ); 403 GNUNET_assert (NULL != j_coin); 404 GNUNET_assert (0 == 405 json_array_append_new (j_envs, j_coin)); 406 } 407 GNUNET_assert (0 == 408 json_array_append_new (j_coin_evs, j_envs)); 409 GNUNET_assert (0 == 410 json_array_append_new (j_transfer_pubs, j_tbs)); 411 } 412 GNUNET_assert (0 == 413 json_object_set_new (j_request_body, 414 "coin_evs", 415 j_coin_evs)); 416 GNUNET_assert (0 == 417 json_object_set_new (j_request_body, 418 "transfer_pubs", 419 j_transfer_pubs)); 420 /* and now we can at last begin the actual request handling */ 421 mh->url = TALER_url_join (mh->exchange_url, 422 "melt", 423 NULL); 424 if (NULL == mh->url) 425 { 426 json_decref (j_request_body); 427 return GNUNET_SYSERR; 428 } 429 eh = TALER_EXCHANGE_curl_easy_get_ (mh->url); 430 if ( (NULL == eh) || 431 (GNUNET_OK != 432 TALER_curl_easy_post (&mh->ctx, 433 eh, 434 j_request_body)) ) 435 { 436 GNUNET_break (0); 437 if (NULL != eh) 438 curl_easy_cleanup (eh); 439 json_decref (j_request_body); 440 return GNUNET_SYSERR; 441 } 442 json_decref (j_request_body); 443 mh->job = GNUNET_CURL_job_add2 (mh->cctx, 444 eh, 445 mh->ctx.headers, 446 &handle_melt_finished, 447 mh); 448 return GNUNET_OK; 449 } 450 451 452 /** 453 * The melt request @a mh failed, return an error to 454 * the application and cancel the operation. 455 * 456 * @param[in] mh melt request that failed 457 * @param ec error code to fail with 458 */ 459 static void 460 fail_mh (struct TALER_EXCHANGE_PostMeltHandle *mh, 461 enum TALER_ErrorCode ec) 462 { 463 struct TALER_EXCHANGE_PostMeltResponse mr = { 464 .hr.ec = ec 465 }; 466 467 mh->melt_cb (mh->melt_cb_cls, 468 &mr); 469 TALER_EXCHANGE_post_melt_cancel (mh); 470 } 471 472 473 /** 474 * Callbacks of this type are used to serve the result of submitting a 475 * /blinding-prepare request to a exchange. 476 * 477 * @param cls closure with our `struct TALER_EXCHANGE_MeltHandle *` 478 * @param bpr response details 479 */ 480 static void 481 blinding_prepare_cb (void *cls, 482 const struct TALER_EXCHANGE_PostBlindingPrepareResponse * 483 bpr) 484 { 485 struct TALER_EXCHANGE_PostMeltHandle *mh = cls; 486 unsigned int nks_off = 0; 487 488 mh->bpr = NULL; 489 if (MHD_HTTP_OK != bpr->hr.http_status) 490 { 491 struct TALER_EXCHANGE_PostMeltResponse mr = { 492 .hr = bpr->hr 493 }; 494 495 mr.hr.hint = "/blinding-prepare failed"; 496 mh->melt_cb (mh->melt_cb_cls, 497 &mr); 498 TALER_EXCHANGE_post_melt_cancel (mh); 499 return; 500 } 501 for (unsigned int i = 0; i<mh->rd->num_fresh_denom_pubs; i++) 502 { 503 const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = 504 &mh->rd->fresh_denom_pubs[i]; 505 struct TALER_ExchangeBlindingValues *wv = &mh->melt_blinding_values[i]; 506 507 switch (fresh_pk->key.bsign_pub_key->cipher) 508 { 509 case GNUNET_CRYPTO_BSA_INVALID: 510 GNUNET_break (0); 511 fail_mh (mh, 512 TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR); 513 return; 514 case GNUNET_CRYPTO_BSA_RSA: 515 break; 516 case GNUNET_CRYPTO_BSA_CS: 517 TALER_denom_ewv_copy (wv, 518 &bpr->details.ok.blinding_values[nks_off]); 519 nks_off++; 520 break; 521 } 522 } 523 if (GNUNET_OK != 524 start_melt (mh)) 525 { 526 GNUNET_break (0); 527 fail_mh (mh, 528 TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR); 529 return; 530 } 531 } 532 533 534 struct TALER_EXCHANGE_PostMeltHandle * 535 TALER_EXCHANGE_post_melt_create ( 536 struct GNUNET_CURL_Context *ctx, 537 const char *url, 538 struct TALER_EXCHANGE_Keys *keys, 539 const struct TALER_PublicRefreshMasterSeedP *rms, 540 const struct TALER_EXCHANGE_MeltInput *rd) 541 { 542 struct TALER_EXCHANGE_PostMeltHandle *mh; 543 544 if (0 == rd->num_fresh_denom_pubs) 545 { 546 GNUNET_break (0); 547 return NULL; 548 } 549 mh = GNUNET_new (struct TALER_EXCHANGE_PostMeltHandle); 550 mh->noreveal_index = TALER_CNC_KAPPA; /* invalid value */ 551 mh->cctx = ctx; 552 mh->exchange_url = GNUNET_strdup (url); 553 mh->rd = rd; 554 mh->rms = *rms; 555 mh->no_blinding_seed = true; 556 mh->melt_blinding_values = 557 GNUNET_new_array (rd->num_fresh_denom_pubs, 558 struct TALER_ExchangeBlindingValues); 559 for (unsigned int i = 0; i < rd->num_fresh_denom_pubs; i++) 560 { 561 const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = 562 &rd->fresh_denom_pubs[i]; 563 564 switch (fresh_pk->key.bsign_pub_key->cipher) 565 { 566 case GNUNET_CRYPTO_BSA_INVALID: 567 GNUNET_break (0); 568 GNUNET_free (mh->melt_blinding_values); 569 GNUNET_free (mh->exchange_url); 570 GNUNET_free (mh); 571 return NULL; 572 case GNUNET_CRYPTO_BSA_RSA: 573 TALER_denom_ewv_copy (&mh->melt_blinding_values[i], 574 TALER_denom_ewv_rsa_singleton ()); 575 break; 576 case GNUNET_CRYPTO_BSA_CS: 577 mh->no_blinding_seed = false; 578 break; 579 } 580 } 581 mh->keys = TALER_EXCHANGE_keys_incref (keys); 582 return mh; 583 } 584 585 586 enum TALER_ErrorCode 587 TALER_EXCHANGE_post_melt_start ( 588 struct TALER_EXCHANGE_PostMeltHandle *mh, 589 TALER_EXCHANGE_PostMeltCallback melt_cb, 590 TALER_EXCHANGE_POST_MELT_RESULT_CLOSURE *melt_cb_cls) 591 { 592 mh->melt_cb = melt_cb; 593 mh->melt_cb_cls = melt_cb_cls; 594 595 if (! mh->no_blinding_seed) 596 { 597 struct TALER_EXCHANGE_NonceKey nks[GNUNET_NZL ( 598 mh->rd->num_fresh_denom_pubs)]; 599 unsigned int nks_off = 0; 600 601 for (unsigned int i = 0; i < mh->rd->num_fresh_denom_pubs; i++) 602 { 603 const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = 604 &mh->rd->fresh_denom_pubs[i]; 605 606 if (GNUNET_CRYPTO_BSA_CS == 607 fresh_pk->key.bsign_pub_key->cipher) 608 { 609 nks[nks_off].pk = fresh_pk; 610 nks[nks_off].cnc_num = i; 611 nks_off++; 612 } 613 } 614 TALER_cs_refresh_seed_to_blinding_seed ( 615 &mh->rms, 616 &mh->md.melted_coin.coin_priv, 617 &mh->blinding_seed); 618 mh->bpr = TALER_EXCHANGE_post_blinding_prepare_for_melt_create ( 619 mh->cctx, 620 mh->exchange_url, 621 &mh->blinding_seed, 622 nks_off, 623 nks); 624 if (NULL == mh->bpr) 625 { 626 GNUNET_break (0); 627 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 628 } 629 { 630 enum TALER_ErrorCode ec; 631 632 ec = TALER_EXCHANGE_post_blinding_prepare_start (mh->bpr, 633 &blinding_prepare_cb, 634 mh); 635 if (TALER_EC_NONE != ec) 636 { 637 GNUNET_break (0); 638 TALER_EXCHANGE_post_blinding_prepare_cancel (mh->bpr); 639 mh->bpr = NULL; 640 return ec; 641 } 642 } 643 return TALER_EC_NONE; 644 } 645 if (GNUNET_OK != 646 start_melt (mh)) 647 { 648 GNUNET_break (0); 649 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 650 } 651 return TALER_EC_NONE; 652 } 653 654 655 void 656 TALER_EXCHANGE_post_melt_cancel (struct TALER_EXCHANGE_PostMeltHandle *mh) 657 { 658 for (unsigned int i = 0; i < mh->rd->num_fresh_denom_pubs; i++) 659 TALER_denom_ewv_free (&mh->melt_blinding_values[i]); 660 if (NULL != mh->job) 661 { 662 GNUNET_CURL_job_cancel (mh->job); 663 mh->job = NULL; 664 } 665 if (NULL != mh->bpr) 666 { 667 TALER_EXCHANGE_post_blinding_prepare_cancel (mh->bpr); 668 mh->bpr = NULL; 669 } 670 TALER_EXCHANGE_free_melt_data (&mh->md); /* does not free 'md' itself */ 671 GNUNET_free (mh->melt_blinding_values); 672 GNUNET_free (mh->url); 673 GNUNET_free (mh->exchange_url); 674 TALER_curl_easy_post_finished (&mh->ctx); 675 TALER_EXCHANGE_keys_decref (mh->keys); 676 GNUNET_free (mh); 677 } 678 679 680 /* end of exchange_api_melt.c */