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 Lesser General Public License as published by the Free Software
7 : Foundation; either version 2.1, 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 Lesser General Public License for more details.
12 :
13 : You should have received a copy of the GNU Lesser General Public License along with
14 : TALER; see the file COPYING.LGPL. If not, see
15 : <http://www.gnu.org/licenses/>
16 : */
17 : /**
18 : * @file merchant_api_tip_pickup.c
19 : * @brief Implementation of the /tip-pickup request of the merchant's HTTP API
20 : * @author Marcello Stanisci
21 : * @author Christian Grothoff
22 : */
23 : #include "platform.h"
24 : #include <curl/curl.h>
25 : #include <jansson.h>
26 : #include <microhttpd.h> /* just for HTTP status codes */
27 : #include <gnunet/gnunet_util_lib.h>
28 : #include <gnunet/gnunet_curl_lib.h>
29 : #include "taler_merchant_service.h"
30 : #include <taler/taler_json_lib.h>
31 : #include <taler/taler_signatures.h>
32 : #include <taler/taler_curl_lib.h>
33 :
34 :
35 : /**
36 : * Data we keep per planchet.
37 : */
38 : struct PlanchetData
39 : {
40 : /**
41 : * Secrets of the planchet.
42 : */
43 : struct TALER_PlanchetMasterSecretP ps;
44 :
45 : /**
46 : * Denomination key we are withdrawing.
47 : */
48 : struct TALER_EXCHANGE_DenomPublicKey pk;
49 :
50 : /**
51 : * Hash of the public key of the coin we are signing.
52 : */
53 : struct TALER_CoinPubHashP c_hash;
54 :
55 : /**
56 : * Nonce used for @e csr request, if any.
57 : */
58 : struct TALER_CsNonce nonce;
59 :
60 : /**
61 : * Handle for a /csr request we may optionally need
62 : * to trigger.
63 : */
64 : struct TALER_EXCHANGE_CsRWithdrawHandle *csr;
65 :
66 : /**
67 : * Handle for the /tip-pickup operation we are part of.
68 : */
69 : struct TALER_MERCHANT_TipPickupHandle *tp;
70 :
71 : /**
72 : * Offset of this entry in the array.
73 : */
74 : unsigned int off;
75 : };
76 :
77 :
78 : /**
79 : * Handle for a /tip-pickup operation.
80 : */
81 : struct TALER_MERCHANT_TipPickupHandle
82 : {
83 :
84 : /**
85 : * Function to call with the result.
86 : */
87 : TALER_MERCHANT_TipPickupCallback cb;
88 :
89 : /**
90 : * Closure for @a cb.
91 : */
92 : void *cb_cls;
93 :
94 : /**
95 : * Handle for the actual (internal) withdraw operation.
96 : */
97 : struct TALER_MERCHANT_TipPickup2Handle *tpo2;
98 :
99 : /**
100 : * Array of length @e num_planchets.
101 : */
102 : struct PlanchetData *planchets;
103 :
104 : /**
105 : * Array of length @e num_planchets.
106 : */
107 : struct TALER_EXCHANGE_PrivateCoinDetails *pcds;
108 :
109 : /**
110 : * Context for making HTTP requests.
111 : */
112 : struct GNUNET_CURL_Context *ctx;
113 :
114 : /**
115 : * URL of the merchant backend.
116 : */
117 : char *backend_url;
118 :
119 : /**
120 : * ID of the tip we are picking up.
121 : */
122 : struct TALER_TipIdentifierP tip_id;
123 :
124 : /**
125 : * Number of planchets/coins used for this operation.
126 : */
127 : unsigned int num_planchets;
128 :
129 : /**
130 : * Number of remaining active /csr-withdraw requests.
131 : */
132 : unsigned int csr_active;
133 : };
134 :
135 :
136 : /**
137 : * Fail the pickup operation @a tp, returning @a ec.
138 : * Also cancels @a tp.
139 : *
140 : * @param[in] tp operation to fail
141 : * @param ec reason for the failure
142 : */
143 : static void
144 0 : fail_pickup (struct TALER_MERCHANT_TipPickupHandle *tp,
145 : enum TALER_ErrorCode ec)
146 : {
147 0 : struct TALER_MERCHANT_PickupDetails pd = {
148 : .hr.ec = ec
149 : };
150 :
151 0 : tp->cb (tp->cb_cls,
152 : &pd);
153 0 : TALER_MERCHANT_tip_pickup_cancel (tp);
154 0 : }
155 :
156 :
157 : /**
158 : * Callback for a /tip-pickup request. Returns the result of the operation.
159 : * Note that the client MUST still do the unblinding of the @a blind_sigs.
160 : *
161 : * @param cls closure, a `struct TALER_MERCHANT_TipPickupHandle *`
162 : * @param hr HTTP response details
163 : * @param num_blind_sigs length of the @a reserve_sigs array, 0 on error
164 : * @param blind_sigs array of blind signatures over the planchets, NULL on error
165 : */
166 : static void
167 0 : pickup_done_cb (void *cls,
168 : const struct TALER_MERCHANT_HttpResponse *hr,
169 : unsigned int num_blind_sigs,
170 : const struct TALER_BlindedDenominationSignature *blind_sigs)
171 : {
172 0 : struct TALER_MERCHANT_TipPickupHandle *tp = cls;
173 0 : struct TALER_MERCHANT_PickupDetails pd = {
174 : .hr = *hr
175 : };
176 :
177 0 : tp->tpo2 = NULL;
178 0 : if (NULL == blind_sigs)
179 : {
180 0 : tp->cb (tp->cb_cls,
181 : &pd);
182 0 : TALER_MERCHANT_tip_pickup_cancel (tp);
183 0 : return;
184 : }
185 : {
186 0 : enum GNUNET_GenericReturnValue ok = GNUNET_OK;
187 :
188 0 : for (unsigned int i = 0; i<num_blind_sigs; i++)
189 : {
190 0 : struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i];
191 : struct TALER_FreshCoin fc;
192 :
193 0 : if (GNUNET_OK !=
194 0 : TALER_planchet_to_coin (&tp->planchets[i].pk.key,
195 0 : &blind_sigs[i],
196 0 : &pcd->bks,
197 0 : &pcd->coin_priv,
198 : NULL,
199 0 : &tp->planchets[i].c_hash,
200 0 : &pcd->exchange_vals,
201 : &fc))
202 : {
203 0 : ok = GNUNET_SYSERR;
204 0 : break;
205 : }
206 0 : pcd->sig = fc.sig;
207 : }
208 0 : if (GNUNET_OK != ok)
209 : {
210 0 : struct TALER_MERCHANT_HttpResponse hrx = {
211 0 : .reply = hr->reply,
212 : .ec = TALER_EC_MERCHANT_TIP_PICKUP_UNBLIND_FAILURE
213 : };
214 :
215 0 : pd.hr = hrx;
216 0 : tp->cb (tp->cb_cls,
217 : &pd);
218 : }
219 : else
220 : {
221 0 : pd.details.success.num_sigs = num_blind_sigs;
222 0 : pd.details.success.pcds = tp->pcds;
223 0 : tp->cb (tp->cb_cls,
224 : &pd);
225 : }
226 : }
227 0 : TALER_MERCHANT_tip_pickup_cancel (tp);
228 : }
229 :
230 :
231 : /**
232 : * We have obtained all of the exchange inputs. Continue the pickup.
233 : *
234 : * @param[in,out] tp operation to continue
235 : */
236 : static void
237 0 : pickup_post_csr (struct TALER_MERCHANT_TipPickupHandle *tp)
238 0 : {
239 0 : struct TALER_PlanchetDetail details[tp->num_planchets];
240 :
241 0 : for (unsigned int i = 0; i<tp->num_planchets; i++)
242 : {
243 0 : const struct PlanchetData *pd = &tp->planchets[i];
244 0 : struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i];
245 :
246 0 : TALER_planchet_setup_coin_priv (&pd->ps,
247 0 : &pcd->exchange_vals,
248 : &pcd->coin_priv);
249 0 : TALER_planchet_blinding_secret_create (&pd->ps,
250 0 : &pcd->exchange_vals,
251 : &pcd->bks);
252 0 : if (TALER_DENOMINATION_CS == pcd->exchange_vals.cipher)
253 : {
254 : details[i].blinded_planchet.details.cs_blinded_planchet.nonce
255 0 : = pd->nonce;
256 : }
257 0 : if (GNUNET_OK !=
258 0 : TALER_planchet_prepare (&pd->pk.key,
259 0 : &pcd->exchange_vals,
260 0 : &pcd->bks,
261 0 : &pcd->coin_priv,
262 : NULL,
263 0 : &tp->planchets[i].c_hash,
264 : &details[i]))
265 : {
266 0 : GNUNET_break (0);
267 0 : for (unsigned int j = 0; j<i; j++)
268 0 : TALER_planchet_detail_free (&details[j]);
269 0 : fail_pickup (tp,
270 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE);
271 0 : return;
272 : }
273 : }
274 0 : tp->tpo2 = TALER_MERCHANT_tip_pickup2 (tp->ctx,
275 0 : tp->backend_url,
276 0 : &tp->tip_id,
277 : tp->num_planchets,
278 : details,
279 : &pickup_done_cb,
280 : tp);
281 0 : for (unsigned int j = 0; j<tp->num_planchets; j++)
282 0 : TALER_planchet_detail_free (&details[j]);
283 0 : if (NULL == tp->tpo2)
284 : {
285 0 : GNUNET_break (0);
286 0 : fail_pickup (tp,
287 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE);
288 0 : return;
289 : }
290 : }
291 :
292 :
293 : /**
294 : * Callbacks of this type are used to serve the result of submitting a
295 : * CS R request to a exchange.
296 : *
297 : * @param cls a `struct TALER_MERCHANT_TipPickupHandle`
298 : * @param csrr response details
299 : */
300 : static void
301 0 : csr_cb (void *cls,
302 : const struct TALER_EXCHANGE_CsRWithdrawResponse *csrr)
303 : {
304 0 : struct PlanchetData *pd = cls;
305 0 : struct TALER_MERCHANT_TipPickupHandle *tp = pd->tp;
306 :
307 0 : pd->csr = NULL;
308 0 : tp->csr_active--;
309 0 : switch (csrr->hr.http_status)
310 : {
311 0 : case MHD_HTTP_OK:
312 : {
313 0 : struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[pd->off];
314 :
315 0 : pcd->exchange_vals = csrr->details.success.alg_values;
316 : }
317 0 : if (0 != tp->csr_active)
318 0 : return;
319 0 : pickup_post_csr (tp);
320 0 : return;
321 0 : default:
322 : {
323 0 : struct TALER_MERCHANT_PickupDetails pd = {
324 : .hr.hint = "/csr-withdraw failed",
325 0 : .hr.exchange_http_status = csrr->hr.http_status
326 : };
327 :
328 0 : tp->cb (tp->cb_cls,
329 : &pd);
330 0 : TALER_MERCHANT_tip_pickup_cancel (tp);
331 0 : return;
332 : }
333 : }
334 : }
335 :
336 :
337 : struct TALER_MERCHANT_TipPickupHandle *
338 0 : TALER_MERCHANT_tip_pickup (struct GNUNET_CURL_Context *ctx,
339 : struct TALER_EXCHANGE_Handle *exchange,
340 : const char *backend_url,
341 : const struct TALER_TipIdentifierP *tip_id,
342 : unsigned int num_planchets,
343 : const struct TALER_MERCHANT_PlanchetData *pds,
344 : TALER_MERCHANT_TipPickupCallback pickup_cb,
345 : void *pickup_cb_cls)
346 : {
347 : struct TALER_MERCHANT_TipPickupHandle *tp;
348 :
349 0 : if (0 == num_planchets)
350 : {
351 0 : GNUNET_break (0);
352 0 : return NULL;
353 : }
354 0 : tp = GNUNET_new (struct TALER_MERCHANT_TipPickupHandle);
355 0 : tp->cb = pickup_cb;
356 0 : tp->cb_cls = pickup_cb_cls;
357 0 : tp->ctx = ctx;
358 0 : tp->backend_url = GNUNET_strdup (backend_url);
359 0 : tp->tip_id = *tip_id;
360 0 : tp->num_planchets = num_planchets;
361 0 : tp->planchets = GNUNET_new_array (num_planchets,
362 : struct PlanchetData);
363 0 : tp->pcds = GNUNET_new_array (num_planchets,
364 : struct TALER_EXCHANGE_PrivateCoinDetails);
365 0 : for (unsigned int i = 0; i<num_planchets; i++)
366 : {
367 0 : const struct TALER_MERCHANT_PlanchetData *mpd = &pds[i];
368 0 : const struct TALER_EXCHANGE_DenomPublicKey *pk = mpd->pk;
369 0 : struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i];
370 0 : struct PlanchetData *pd = &tp->planchets[i];
371 :
372 0 : pd->off = i;
373 0 : pd->tp = tp;
374 0 : tp->planchets[i].ps = mpd->ps;
375 0 : tp->planchets[i].pk = *pds[i].pk;
376 0 : TALER_denom_pub_deep_copy (&tp->planchets[i].pk.key,
377 0 : &pds[i].pk->key);
378 0 : switch (pk->key.cipher)
379 : {
380 0 : case TALER_DENOMINATION_RSA:
381 0 : pcd->exchange_vals.cipher = TALER_DENOMINATION_RSA;
382 0 : break;
383 0 : case TALER_DENOMINATION_CS:
384 : {
385 0 : TALER_cs_withdraw_nonce_derive (&pd->ps,
386 : &pd->nonce);
387 0 : pd->csr = TALER_EXCHANGE_csr_withdraw (exchange,
388 0 : &pd->pk,
389 0 : &pd->nonce,
390 : &csr_cb,
391 : pd);
392 0 : if (NULL == pd->csr)
393 : {
394 0 : GNUNET_break (0);
395 0 : TALER_MERCHANT_tip_pickup_cancel (tp);
396 0 : return NULL;
397 : }
398 0 : tp->csr_active++;
399 0 : break;
400 : }
401 0 : default:
402 0 : GNUNET_break (0);
403 0 : TALER_MERCHANT_tip_pickup_cancel (tp);
404 0 : return NULL;
405 : }
406 : }
407 0 : if (0 == tp->csr_active)
408 : {
409 0 : pickup_post_csr (tp);
410 0 : return tp;
411 : }
412 0 : return tp;
413 : }
414 :
415 :
416 : void
417 0 : TALER_MERCHANT_tip_pickup_cancel (struct TALER_MERCHANT_TipPickupHandle *tp)
418 : {
419 0 : for (unsigned int i = 0; i<tp->num_planchets; i++)
420 : {
421 0 : struct TALER_EXCHANGE_PrivateCoinDetails *pcd = &tp->pcds[i];
422 0 : struct PlanchetData *pd = &tp->planchets[i];
423 :
424 0 : TALER_denom_sig_free (&pcd->sig);
425 0 : TALER_denom_pub_free (&tp->planchets[i].pk.key);
426 0 : if (NULL != pd->csr)
427 : {
428 0 : TALER_EXCHANGE_csr_withdraw_cancel (pd->csr);
429 0 : pd->csr = NULL;
430 : }
431 : }
432 0 : GNUNET_array_grow (tp->planchets,
433 : tp->num_planchets,
434 : 0);
435 0 : if (NULL != tp->tpo2)
436 : {
437 0 : TALER_MERCHANT_tip_pickup2_cancel (tp->tpo2);
438 0 : tp->tpo2 = NULL;
439 : }
440 0 : GNUNET_free (tp->backend_url);
441 0 : GNUNET_free (tp->pcds);
442 0 : GNUNET_free (tp);
443 0 : }
444 :
445 :
446 : /* end of merchant_api_tip_pickup.c */
|