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