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_blinding_prepare.c
19 : * @brief Implementation of /blinding-prepare requests
20 : * @author Özgür Kesim
21 : */
22 :
23 : #include "platform.h"
24 : #include <gnunet/gnunet_common.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_json_lib.h>
29 : #include <gnunet/gnunet_curl_lib.h>
30 : #include <sys/wait.h>
31 : #include "taler_curl_lib.h"
32 : #include "taler_error_codes.h"
33 : #include "taler_json_lib.h"
34 : #include "taler_exchange_service.h"
35 : #include "exchange_api_common.h"
36 : #include "exchange_api_handle.h"
37 : #include "taler_signatures.h"
38 : #include "exchange_api_curl_defaults.h"
39 : #include "taler_util.h"
40 :
41 : /**
42 : * A /blinding-prepare request-handle
43 : */
44 : struct TALER_EXCHANGE_BlindingPrepareHandle
45 : {
46 : /**
47 : * number of elements to prepare
48 : */
49 : size_t num;
50 :
51 : /**
52 : * True, if this operation is for melting (or withdraw otherwise).
53 : */
54 : bool for_melt;
55 :
56 : /**
57 : * The seed for the batch of nonces.
58 : */
59 : const struct TALER_BlindingMasterSeedP *seed;
60 :
61 : /**
62 : * The url for this request.
63 : */
64 : char *url;
65 :
66 : /**
67 : * Context for curl.
68 : */
69 : struct GNUNET_CURL_Context *curl_ctx;
70 :
71 : /**
72 : * CURL handle for the request job.
73 : */
74 : struct GNUNET_CURL_Job *job;
75 :
76 : /**
77 : * Post Context
78 : */
79 : struct TALER_CURL_PostContext post_ctx;
80 :
81 : /**
82 : * Function to call with withdraw response results.
83 : */
84 : TALER_EXCHANGE_BlindingPrepareCallback callback;
85 :
86 : /**
87 : * Closure for @e callback
88 : */
89 : void *callback_cls;
90 :
91 : };
92 :
93 :
94 : /**
95 : * We got a 200 OK response for the /blinding-prepare operation.
96 : * Extract the r_pub values and return them to the caller via the callback
97 : *
98 : * @param handle operation handle
99 : * @param response response details
100 : * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
101 : */
102 : static enum GNUNET_GenericReturnValue
103 50 : blinding_prepare_ok (struct TALER_EXCHANGE_BlindingPrepareHandle *handle,
104 : struct TALER_EXCHANGE_BlindingPrepareResponse *response)
105 : {
106 : const json_t *j_r_pubs;
107 : const char *cipher;
108 : struct GNUNET_JSON_Specification spec[] = {
109 50 : GNUNET_JSON_spec_string ("cipher",
110 : &cipher),
111 50 : GNUNET_JSON_spec_array_const ("r_pubs",
112 : &j_r_pubs),
113 50 : GNUNET_JSON_spec_end ()
114 : };
115 :
116 50 : if (GNUNET_OK !=
117 50 : GNUNET_JSON_parse (response->hr.reply,
118 : spec,
119 : NULL, NULL))
120 : {
121 0 : GNUNET_break_op (0);
122 0 : return GNUNET_SYSERR;
123 : }
124 :
125 50 : if (strcmp ("CS", cipher))
126 : {
127 0 : GNUNET_break_op (0);
128 0 : return GNUNET_SYSERR;
129 : }
130 :
131 50 : if (json_array_size (j_r_pubs)
132 50 : != handle->num)
133 : {
134 0 : GNUNET_break_op (0);
135 0 : return GNUNET_SYSERR;
136 : }
137 :
138 50 : {
139 50 : size_t num = handle->num;
140 : const json_t *j_pair;
141 : size_t idx;
142 50 : struct TALER_ExchangeBlindingValues blinding_values[GNUNET_NZL (num)];
143 :
144 50 : memset (blinding_values,
145 : 0,
146 : sizeof(blinding_values));
147 :
148 148 : json_array_foreach (j_r_pubs, idx, j_pair) {
149 : struct GNUNET_CRYPTO_BlindingInputValues *bi =
150 98 : GNUNET_new (struct GNUNET_CRYPTO_BlindingInputValues);
151 98 : struct GNUNET_CRYPTO_CSPublicRPairP *csv = &bi->details.cs_values;
152 : struct GNUNET_JSON_Specification tuple[] = {
153 98 : GNUNET_JSON_spec_fixed (NULL,
154 98 : &csv->r_pub[0],
155 : sizeof(csv->r_pub[0])),
156 98 : GNUNET_JSON_spec_fixed (NULL,
157 98 : &csv->r_pub[1],
158 : sizeof(csv->r_pub[1])),
159 98 : GNUNET_JSON_spec_end ()
160 : };
161 : struct GNUNET_JSON_Specification jspec[] = {
162 98 : TALER_JSON_spec_tuple_of (NULL, tuple),
163 98 : GNUNET_JSON_spec_end ()
164 : };
165 : const char *err_msg;
166 : unsigned int err_line;
167 :
168 98 : if (GNUNET_OK !=
169 98 : GNUNET_JSON_parse (j_pair,
170 : jspec,
171 : &err_msg,
172 : &err_line))
173 : {
174 0 : GNUNET_break_op (0);
175 0 : GNUNET_free (bi);
176 0 : for (size_t i=0; i < idx; i++)
177 0 : TALER_denom_ewv_free (&blinding_values[i]);
178 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
179 : "Error while parsing response: in line %d: %s",
180 : err_line,
181 : err_msg);
182 0 : return GNUNET_SYSERR;
183 : }
184 :
185 98 : bi->cipher = GNUNET_CRYPTO_BSA_CS;
186 98 : bi->rc = 1;
187 98 : blinding_values[idx].blinding_inputs = bi;
188 : }
189 :
190 50 : response->details.ok.blinding_values = blinding_values;
191 50 : response->details.ok.num_blinding_values = num;
192 :
193 50 : handle->callback (
194 : handle->callback_cls,
195 : response);
196 :
197 148 : for (size_t i = 0; i < num; i++)
198 98 : TALER_denom_ewv_free (&blinding_values[i]);
199 : }
200 50 : return GNUNET_OK;
201 : }
202 :
203 :
204 : /**
205 : * Function called when we're done processing the HTTP /blinding-prepare request.
206 : *
207 : * @param cls the `struct TALER_EXCHANGE_BlindingPrepareHandle`
208 : * @param response_code HTTP response code, 0 on error
209 : * @param response parsed JSON result, NULL on error
210 : */
211 : static void
212 50 : handle_blinding_prepare_finished (void *cls,
213 : long response_code,
214 : const void *response)
215 : {
216 50 : struct TALER_EXCHANGE_BlindingPrepareHandle *handle = cls;
217 50 : const json_t *j_response = response;
218 50 : struct TALER_EXCHANGE_BlindingPrepareResponse bpr = {
219 : .hr = {
220 : .reply = j_response,
221 50 : .http_status = (unsigned int) response_code
222 : },
223 : };
224 :
225 50 : handle->job = NULL;
226 :
227 50 : switch (response_code)
228 : {
229 0 : case 0:
230 0 : bpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
231 0 : break;
232 :
233 50 : case MHD_HTTP_OK:
234 : {
235 50 : if (GNUNET_OK !=
236 50 : blinding_prepare_ok (handle,
237 : &bpr))
238 : {
239 0 : GNUNET_break_op (0);
240 0 : bpr.hr.http_status = 0;
241 0 : bpr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
242 0 : break;
243 : }
244 : }
245 50 : TALER_EXCHANGE_blinding_prepare_cancel (handle);
246 50 : return;
247 :
248 0 : case MHD_HTTP_BAD_REQUEST:
249 : /* This should never happen, either us or the exchange is buggy
250 : (or API version conflict); just pass JSON reply to the application */
251 0 : bpr.hr.ec = TALER_JSON_get_error_code (j_response);
252 0 : bpr.hr.hint = TALER_JSON_get_error_hint (j_response);
253 0 : break;
254 :
255 0 : case MHD_HTTP_NOT_FOUND:
256 : /* Nothing really to verify, the exchange basically just says
257 : that it doesn't know the /csr endpoint or denomination.
258 : Can happen if the exchange doesn't support Clause Schnorr.
259 : We should simply pass the JSON reply to the application. */
260 0 : bpr.hr.ec = TALER_JSON_get_error_code (j_response);
261 0 : bpr.hr.hint = TALER_JSON_get_error_hint (j_response);
262 0 : break;
263 :
264 0 : case MHD_HTTP_GONE:
265 : /* could happen if denomination was revoked */
266 : /* Note: one might want to check /keys for revocation
267 : signature here, alas tricky in case our /keys
268 : is outdated => left to clients */
269 0 : bpr.hr.ec = TALER_JSON_get_error_code (j_response);
270 0 : bpr.hr.hint = TALER_JSON_get_error_hint (j_response);
271 0 : break;
272 :
273 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
274 : /* Server had an internal issue; we should retry, but this API
275 : leaves this to the application */
276 0 : bpr.hr.ec = TALER_JSON_get_error_code (j_response);
277 0 : bpr.hr.hint = TALER_JSON_get_error_hint (j_response);
278 0 : break;
279 :
280 0 : default:
281 : /* unexpected response code */
282 0 : GNUNET_break_op (0);
283 0 : bpr.hr.ec = TALER_JSON_get_error_code (j_response);
284 0 : bpr.hr.hint = TALER_JSON_get_error_hint (j_response);
285 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
286 : "Unexpected response code %u/%d for the blinding-prepare request\n",
287 : (unsigned int) response_code,
288 : (int) bpr.hr.ec);
289 0 : break;
290 :
291 : }
292 :
293 0 : handle->callback (handle->callback_cls,
294 : &bpr);
295 0 : handle->callback = NULL;
296 0 : TALER_EXCHANGE_blinding_prepare_cancel (handle);
297 : }
298 :
299 :
300 : struct TALER_EXCHANGE_BlindingPrepareHandle *
301 50 : TALER_EXCHANGE_blinding_prepare (
302 : struct GNUNET_CURL_Context *curl_ctx,
303 : const char *exchange_url,
304 : const struct TALER_BlindingMasterSeedP *seed,
305 : bool for_melt,
306 : size_t num,
307 : const struct TALER_EXCHANGE_NonceKey nonce_keys[static num],
308 : TALER_EXCHANGE_BlindingPrepareCallback callback,
309 : void *callback_cls)
310 50 : {
311 : struct TALER_EXCHANGE_BlindingPrepareHandle *bph;
312 :
313 50 : if (0 == num)
314 : {
315 0 : GNUNET_break (0);
316 0 : return NULL;
317 : }
318 148 : for (unsigned int i = 0; i<num; i++)
319 98 : if (GNUNET_CRYPTO_BSA_CS !=
320 98 : nonce_keys[i].pk->key.bsign_pub_key->cipher)
321 : {
322 0 : GNUNET_break (0);
323 0 : return NULL;
324 : }
325 50 : bph = GNUNET_new (struct TALER_EXCHANGE_BlindingPrepareHandle);
326 50 : bph->num = num;
327 50 : bph->callback = callback;
328 50 : bph->for_melt = for_melt;
329 50 : bph->callback_cls = callback_cls;
330 50 : bph->url = TALER_url_join (exchange_url,
331 : "blinding-prepare",
332 : NULL);
333 50 : if (NULL == bph->url)
334 : {
335 0 : GNUNET_break (0);
336 0 : GNUNET_free (bph);
337 0 : return NULL;
338 : }
339 : {
340 : CURL *eh;
341 : json_t *j_nks;
342 50 : json_t *j_request = GNUNET_JSON_PACK (
343 : GNUNET_JSON_pack_string ("cipher",
344 : "CS"),
345 : GNUNET_JSON_pack_string ("operation",
346 : for_melt ? "melt" : "withdraw"),
347 : GNUNET_JSON_pack_data_auto ("seed",
348 : seed));
349 50 : GNUNET_assert (NULL != j_request);
350 :
351 50 : j_nks = json_array ();
352 50 : GNUNET_assert (NULL != j_nks);
353 :
354 148 : for (size_t i = 0; i<num; i++)
355 : {
356 98 : const struct TALER_EXCHANGE_NonceKey *nk = &nonce_keys[i];
357 98 : json_t *j_entry = GNUNET_JSON_PACK (
358 : GNUNET_JSON_pack_uint64 ("coin_offset",
359 : nk->cnc_num),
360 : GNUNET_JSON_pack_data_auto ("denom_pub_hash",
361 : &nk->pk->h_key));
362 :
363 98 : GNUNET_assert (NULL != j_entry);
364 98 : GNUNET_assert (0 ==
365 : json_array_append_new (j_nks,
366 : j_entry));
367 : }
368 50 : GNUNET_assert (0 ==
369 : json_object_set_new (j_request,
370 : "nks",
371 : j_nks));
372 50 : eh = TALER_EXCHANGE_curl_easy_get_ (bph->url);
373 100 : if ( (NULL == eh) ||
374 : (GNUNET_OK !=
375 50 : TALER_curl_easy_post (&bph->post_ctx,
376 : eh,
377 : j_request)))
378 : {
379 0 : GNUNET_break (0);
380 0 : if (NULL != eh)
381 0 : curl_easy_cleanup (eh);
382 0 : json_decref (j_request);
383 0 : GNUNET_free (bph->url);
384 0 : GNUNET_free (bph);
385 0 : return NULL;
386 : }
387 :
388 50 : json_decref (j_request);
389 100 : bph->job = GNUNET_CURL_job_add2 (curl_ctx,
390 : eh,
391 50 : bph->post_ctx.headers,
392 : &handle_blinding_prepare_finished,
393 : bph);
394 50 : if (NULL == bph->job)
395 : {
396 0 : GNUNET_break (0);
397 0 : TALER_EXCHANGE_blinding_prepare_cancel (bph);
398 0 : return NULL;
399 : }
400 : }
401 50 : return bph;
402 : }
403 :
404 :
405 : void
406 125 : TALER_EXCHANGE_blinding_prepare_cancel (
407 : struct TALER_EXCHANGE_BlindingPrepareHandle *bph)
408 : {
409 125 : if (NULL == bph)
410 75 : return;
411 50 : if (NULL != bph->job)
412 : {
413 0 : GNUNET_CURL_job_cancel (bph->job);
414 0 : bph->job = NULL;
415 : }
416 50 : GNUNET_free (bph->url);
417 50 : TALER_curl_easy_post_finished (&bph->post_ctx);
418 50 : GNUNET_free (bph);
419 : }
420 :
421 :
422 : /* end of lib/exchange_api_blinding_prepare.c */
|