Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2022 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_batch_withdraw.c
19 : * @brief Implementation of /reserves/$RESERVE_PUB/batch-withdraw requests with blinding/unblinding
20 : * @author Christian Grothoff
21 : */
22 : #include "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_exchange_service.h"
29 : #include "taler_json_lib.h"
30 : #include "exchange_api_handle.h"
31 : #include "taler_signatures.h"
32 : #include "exchange_api_curl_defaults.h"
33 :
34 :
35 : /**
36 : * Data we keep per coin in the batch.
37 : */
38 : struct CoinData
39 : {
40 :
41 : /**
42 : * Denomination key we are withdrawing.
43 : */
44 : struct TALER_EXCHANGE_DenomPublicKey pk;
45 :
46 : /**
47 : * Master key material for the coin.
48 : */
49 : struct TALER_PlanchetMasterSecretP ps;
50 :
51 : /**
52 : * Age commitment for the coin.
53 : */
54 : const struct TALER_AgeCommitmentHash *ach;
55 :
56 : /**
57 : * blinding secret
58 : */
59 : union TALER_DenominationBlindingKeyP bks;
60 :
61 : /**
62 : * Private key of the coin we are withdrawing.
63 : */
64 : struct TALER_CoinSpendPrivateKeyP priv;
65 :
66 : /**
67 : * Details of the planchet.
68 : */
69 : struct TALER_PlanchetDetail pd;
70 :
71 : /**
72 : * Values of the @cipher selected
73 : */
74 : struct TALER_ExchangeWithdrawValues alg_values;
75 :
76 : /**
77 : * Hash of the public key of the coin we are signing.
78 : */
79 : struct TALER_CoinPubHashP c_hash;
80 :
81 : /**
82 : * Handler for the CS R request (only used for TALER_DENOMINATION_CS denominations)
83 : */
84 : struct TALER_EXCHANGE_CsRWithdrawHandle *csrh;
85 :
86 : /**
87 : * Batch withdraw this coin is part of.
88 : */
89 : struct TALER_EXCHANGE_BatchWithdrawHandle *wh;
90 : };
91 :
92 :
93 : /**
94 : * @brief A batch withdraw handle
95 : */
96 : struct TALER_EXCHANGE_BatchWithdrawHandle
97 : {
98 :
99 : /**
100 : * The connection to exchange this request handle will use
101 : */
102 : struct TALER_EXCHANGE_Handle *exchange;
103 :
104 : /**
105 : * Handle for the actual (internal) batch withdraw operation.
106 : */
107 : struct TALER_EXCHANGE_BatchWithdraw2Handle *wh2;
108 :
109 : /**
110 : * Function to call with the result.
111 : */
112 : TALER_EXCHANGE_BatchWithdrawCallback cb;
113 :
114 : /**
115 : * Closure for @a cb.
116 : */
117 : void *cb_cls;
118 :
119 : /**
120 : * Reserve private key.
121 : */
122 : const struct TALER_ReservePrivateKeyP *reserve_priv;
123 :
124 : /**
125 : * Array of per-coin data.
126 : */
127 : struct CoinData *coins;
128 :
129 : /**
130 : * Length of the @e coins array.
131 : */
132 : unsigned int num_coins;
133 :
134 : /**
135 : * Number of CS requests still pending.
136 : */
137 : unsigned int cs_pending;
138 :
139 : };
140 :
141 :
142 : /**
143 : * Function called when we're done processing the
144 : * HTTP /reserves/$RESERVE_PUB/batch-withdraw request.
145 : *
146 : * @param cls the `struct TALER_EXCHANGE_BatchWithdrawHandle`
147 : * @param hr HTTP response data
148 : * @param blind_sigs array of blind signatures over the coins, NULL on error
149 : * @param blind_sigs_length length of the @a blind_sigs array
150 : */
151 : static void
152 0 : handle_reserve_batch_withdraw_finished (
153 : void *cls,
154 : const struct TALER_EXCHANGE_HttpResponse *hr,
155 : const struct TALER_BlindedDenominationSignature *blind_sigs,
156 : unsigned int blind_sigs_length)
157 0 : {
158 0 : struct TALER_EXCHANGE_BatchWithdrawHandle *wh = cls;
159 0 : struct TALER_EXCHANGE_BatchWithdrawResponse wr = {
160 : .hr = *hr
161 : };
162 0 : struct TALER_EXCHANGE_PrivateCoinDetails coins[wh->num_coins];
163 :
164 0 : wh->wh2 = NULL;
165 0 : memset (coins,
166 : 0,
167 : sizeof (coins));
168 0 : if (blind_sigs_length != wh->num_coins)
169 : {
170 0 : GNUNET_break_op (0);
171 0 : wr.hr.http_status = 0;
172 0 : wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
173 : }
174 0 : switch (hr->http_status)
175 : {
176 0 : case MHD_HTTP_OK:
177 : {
178 0 : for (unsigned int i = 0; i<wh->num_coins; i++)
179 : {
180 0 : struct CoinData *cd = &wh->coins[i];
181 0 : struct TALER_EXCHANGE_PrivateCoinDetails *coin = &coins[i];
182 : struct TALER_FreshCoin fc;
183 :
184 0 : if (GNUNET_OK !=
185 0 : TALER_planchet_to_coin (&cd->pk.key,
186 0 : &blind_sigs[i],
187 0 : &cd->bks,
188 0 : &cd->priv,
189 : cd->ach,
190 0 : &cd->c_hash,
191 0 : &cd->alg_values,
192 : &fc))
193 : {
194 0 : wr.hr.http_status = 0;
195 0 : wr.hr.ec = TALER_EC_EXCHANGE_WITHDRAW_UNBLIND_FAILURE;
196 0 : break;
197 : }
198 0 : coin->coin_priv = cd->priv;
199 0 : coin->bks = cd->bks;
200 0 : coin->sig = fc.sig;
201 0 : coin->exchange_vals = cd->alg_values;
202 : }
203 0 : wr.details.success.coins = coins;
204 0 : wr.details.success.num_coins = wh->num_coins;
205 0 : break;
206 : }
207 0 : case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
208 : {
209 : struct GNUNET_JSON_Specification spec[] = {
210 0 : GNUNET_JSON_spec_fixed_auto (
211 : "h_payto",
212 : &wr.details.unavailable_for_legal_reasons.h_payto),
213 0 : GNUNET_JSON_spec_uint64 (
214 : "requirement_row",
215 : &wr.details.unavailable_for_legal_reasons.requirement_row),
216 0 : GNUNET_JSON_spec_end ()
217 : };
218 :
219 0 : if (GNUNET_OK !=
220 0 : GNUNET_JSON_parse (hr->reply,
221 : spec,
222 : NULL, NULL))
223 : {
224 0 : GNUNET_break_op (0);
225 0 : wr.hr.http_status = 0;
226 0 : wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
227 0 : break;
228 : }
229 : }
230 0 : break;
231 0 : default:
232 0 : break;
233 : }
234 0 : wh->cb (wh->cb_cls,
235 : &wr);
236 0 : for (unsigned int i = 0; i<wh->num_coins; i++)
237 0 : TALER_denom_sig_free (&coins[i].sig);
238 0 : TALER_EXCHANGE_batch_withdraw_cancel (wh);
239 0 : }
240 :
241 :
242 : /**
243 : * Runs phase two, the actual withdraw operation.
244 : * Started once the preparation for CS-denominations is
245 : * done.
246 : *
247 : * @param[in,out] wh batch withdraw to start phase 2 for
248 : */
249 : static void
250 0 : phase_two (struct TALER_EXCHANGE_BatchWithdrawHandle *wh)
251 0 : {
252 0 : struct TALER_PlanchetDetail pds[wh->num_coins];
253 :
254 0 : for (unsigned int i = 0; i<wh->num_coins; i++)
255 : {
256 0 : struct CoinData *cd = &wh->coins[i];
257 :
258 0 : pds[i] = cd->pd;
259 : }
260 0 : wh->wh2 = TALER_EXCHANGE_batch_withdraw2 (
261 : wh->exchange,
262 : wh->reserve_priv,
263 : pds,
264 : wh->num_coins,
265 : &handle_reserve_batch_withdraw_finished,
266 : wh);
267 0 : }
268 :
269 :
270 : /**
271 : * Function called when stage 1 of CS withdraw is finished (request r_pub's)
272 : *
273 : * @param cls the `struct CoinData *`
274 : * @param csrr replies from the /csr-withdraw request
275 : */
276 : static void
277 0 : withdraw_cs_stage_two_callback (
278 : void *cls,
279 : const struct TALER_EXCHANGE_CsRWithdrawResponse *csrr)
280 : {
281 0 : struct CoinData *cd = cls;
282 0 : struct TALER_EXCHANGE_BatchWithdrawHandle *wh = cd->wh;
283 0 : struct TALER_EXCHANGE_BatchWithdrawResponse wr = {
284 : .hr = csrr->hr
285 : };
286 :
287 0 : cd->csrh = NULL;
288 0 : GNUNET_assert (TALER_DENOMINATION_CS == cd->pk.key.cipher);
289 0 : switch (csrr->hr.http_status)
290 : {
291 0 : case MHD_HTTP_OK:
292 0 : cd->alg_values = csrr->details.success.alg_values;
293 0 : TALER_planchet_setup_coin_priv (&cd->ps,
294 0 : &cd->alg_values,
295 : &cd->priv);
296 0 : TALER_planchet_blinding_secret_create (&cd->ps,
297 0 : &cd->alg_values,
298 : &cd->bks);
299 : /* This initializes the 2nd half of the
300 : wh->pd.blinded_planchet! */
301 0 : if (GNUNET_OK !=
302 0 : TALER_planchet_prepare (&cd->pk.key,
303 0 : &cd->alg_values,
304 0 : &cd->bks,
305 0 : &cd->priv,
306 : cd->ach,
307 : &cd->c_hash,
308 : &cd->pd))
309 : {
310 0 : GNUNET_break (0);
311 0 : TALER_EXCHANGE_batch_withdraw_cancel (wh);
312 : }
313 0 : wh->cs_pending--;
314 0 : if (0 == wh->cs_pending)
315 0 : phase_two (wh);
316 0 : return;
317 0 : default:
318 0 : break;
319 : }
320 0 : wh->cb (wh->cb_cls,
321 : &wr);
322 0 : TALER_EXCHANGE_batch_withdraw_cancel (wh);
323 : }
324 :
325 :
326 : struct TALER_EXCHANGE_BatchWithdrawHandle *
327 0 : TALER_EXCHANGE_batch_withdraw (
328 : struct TALER_EXCHANGE_Handle *exchange,
329 : const struct TALER_ReservePrivateKeyP *reserve_priv,
330 : const struct TALER_EXCHANGE_WithdrawCoinInput *wcis,
331 : unsigned int wci_length,
332 : TALER_EXCHANGE_BatchWithdrawCallback res_cb,
333 : void *res_cb_cls)
334 : {
335 : struct TALER_EXCHANGE_BatchWithdrawHandle *wh;
336 :
337 0 : wh = GNUNET_new (struct TALER_EXCHANGE_BatchWithdrawHandle);
338 0 : wh->exchange = exchange;
339 0 : wh->cb = res_cb;
340 0 : wh->cb_cls = res_cb_cls;
341 0 : wh->reserve_priv = reserve_priv;
342 0 : wh->num_coins = wci_length;
343 0 : wh->coins = GNUNET_new_array (wh->num_coins,
344 : struct CoinData);
345 0 : for (unsigned int i = 0; i<wci_length; i++)
346 : {
347 0 : struct CoinData *cd = &wh->coins[i];
348 0 : const struct TALER_EXCHANGE_WithdrawCoinInput *wci = &wcis[i];
349 :
350 0 : cd->wh = wh;
351 0 : cd->ps = *wci->ps;
352 0 : cd->ach = wci->ach;
353 0 : cd->pk = *wci->pk;
354 0 : TALER_denom_pub_deep_copy (&cd->pk.key,
355 0 : &wci->pk->key);
356 0 : switch (wci->pk->key.cipher)
357 : {
358 0 : case TALER_DENOMINATION_RSA:
359 : {
360 0 : cd->alg_values.cipher = TALER_DENOMINATION_RSA;
361 0 : TALER_planchet_setup_coin_priv (&cd->ps,
362 0 : &cd->alg_values,
363 : &cd->priv);
364 0 : TALER_planchet_blinding_secret_create (&cd->ps,
365 0 : &cd->alg_values,
366 : &cd->bks);
367 0 : if (GNUNET_OK !=
368 0 : TALER_planchet_prepare (&cd->pk.key,
369 0 : &cd->alg_values,
370 0 : &cd->bks,
371 0 : &cd->priv,
372 : cd->ach,
373 : &cd->c_hash,
374 : &cd->pd))
375 : {
376 0 : GNUNET_break (0);
377 0 : TALER_EXCHANGE_batch_withdraw_cancel (wh);
378 0 : return NULL;
379 : }
380 0 : break;
381 : }
382 0 : case TALER_DENOMINATION_CS:
383 : {
384 0 : TALER_cs_withdraw_nonce_derive (
385 0 : &cd->ps,
386 : &cd->pd.blinded_planchet.details.cs_blinded_planchet.nonce);
387 : /* Note that we only initialize the first half
388 : of the blinded_planchet here; the other part
389 : will be done after the /csr-withdraw request! */
390 0 : cd->pd.blinded_planchet.cipher = TALER_DENOMINATION_CS;
391 0 : cd->csrh = TALER_EXCHANGE_csr_withdraw (
392 : exchange,
393 0 : &cd->pk,
394 0 : &cd->pd.blinded_planchet.details.cs_blinded_planchet.nonce,
395 : &withdraw_cs_stage_two_callback,
396 : cd);
397 0 : if (NULL == cd->csrh)
398 : {
399 0 : GNUNET_break (0);
400 0 : TALER_EXCHANGE_batch_withdraw_cancel (wh);
401 0 : return NULL;
402 : }
403 0 : wh->cs_pending++;
404 0 : break;
405 : }
406 0 : default:
407 0 : GNUNET_break (0);
408 0 : TALER_EXCHANGE_batch_withdraw_cancel (wh);
409 0 : return NULL;
410 : }
411 : }
412 0 : if (0 == wh->cs_pending)
413 0 : phase_two (wh);
414 0 : return wh;
415 : }
416 :
417 :
418 : void
419 0 : TALER_EXCHANGE_batch_withdraw_cancel (
420 : struct TALER_EXCHANGE_BatchWithdrawHandle *wh)
421 : {
422 0 : for (unsigned int i = 0; i<wh->num_coins; i++)
423 : {
424 0 : struct CoinData *cd = &wh->coins[i];
425 :
426 0 : if (NULL != cd->csrh)
427 : {
428 0 : TALER_EXCHANGE_csr_withdraw_cancel (cd->csrh);
429 0 : cd->csrh = NULL;
430 : }
431 0 : TALER_blinded_planchet_free (&cd->pd.blinded_planchet);
432 0 : TALER_denom_pub_free (&cd->pk.key);
433 : }
434 0 : GNUNET_free (wh->coins);
435 0 : if (NULL != wh->wh2)
436 : {
437 0 : TALER_EXCHANGE_batch_withdraw2_cancel (wh->wh2);
438 0 : wh->wh2 = NULL;
439 : }
440 0 : GNUNET_free (wh);
441 0 : }
|