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_csr_melt.c
19 : * @brief Implementation of /csr-melt requests (get R in exchange used for Clause Schnorr refresh)
20 : * @author Lucien Heuzeveldt
21 : * @author Gian Demarmels
22 : */
23 : #include "platform.h"
24 : #include <jansson.h>
25 : #include <microhttpd.h> /* just for HTTP status codes */
26 : #include <gnunet/gnunet_util_lib.h>
27 : #include <gnunet/gnunet_json_lib.h>
28 : #include <gnunet/gnunet_curl_lib.h>
29 : #include "taler_exchange_service.h"
30 : #include "taler_json_lib.h"
31 : #include "exchange_api_handle.h"
32 : #include "taler_signatures.h"
33 : #include "exchange_api_curl_defaults.h"
34 :
35 :
36 : /**
37 : * @brief A Clause Schnorr R Handle
38 : */
39 : struct TALER_EXCHANGE_CsRMeltHandle
40 : {
41 : /**
42 : * The connection to exchange this request handle will use
43 : */
44 : struct TALER_EXCHANGE_Handle *exchange;
45 :
46 : /**
47 : * Function to call with the result.
48 : */
49 : TALER_EXCHANGE_CsRMeltCallback cb;
50 :
51 : /**
52 : * Closure for @a cb.
53 : */
54 : void *cb_cls;
55 :
56 : /**
57 : * The url for this request.
58 : */
59 : char *url;
60 :
61 : /**
62 : * Handle for the request.
63 : */
64 : struct GNUNET_CURL_Job *job;
65 :
66 : /**
67 : * Context for #TEH_curl_easy_post(). Keeps the data that must
68 : * persist for Curl to make the upload.
69 : */
70 : struct TALER_CURL_PostContext post_ctx;
71 : };
72 :
73 :
74 : /**
75 : * We got a 200 OK response for the /reserves/$RESERVE_PUB/withdraw operation.
76 : * Extract the coin's signature and return it to the caller. The signature we
77 : * get from the exchange is for the blinded value. Thus, we first must
78 : * unblind it and then should verify its validity against our coin's hash.
79 : *
80 : * If everything checks out, we return the unblinded signature
81 : * to the application via the callback.
82 : *
83 : * @param csrh operation handle
84 : * @param arr reply from the exchange
85 : * @param hr http response details
86 : * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
87 : */
88 : static enum GNUNET_GenericReturnValue
89 0 : csr_ok (struct TALER_EXCHANGE_CsRMeltHandle *csrh,
90 : const json_t *arr,
91 : struct TALER_EXCHANGE_HttpResponse *hr)
92 0 : {
93 0 : unsigned int alen = json_array_size (arr);
94 0 : struct TALER_ExchangeWithdrawValues alg_values[GNUNET_NZL (alen)];
95 0 : struct TALER_EXCHANGE_CsRMeltResponse csrr = {
96 : .hr = *hr,
97 : .details.success.alg_values_len = alen,
98 : .details.success.alg_values = alg_values
99 : };
100 :
101 0 : for (unsigned int i = 0; i<alen; i++)
102 : {
103 0 : json_t *av = json_array_get (arr,
104 : i);
105 : struct GNUNET_JSON_Specification spec[] = {
106 0 : TALER_JSON_spec_exchange_withdraw_values (
107 : "ewv",
108 : &alg_values[i]),
109 0 : GNUNET_JSON_spec_end ()
110 : };
111 :
112 0 : if (GNUNET_OK !=
113 0 : GNUNET_JSON_parse (av,
114 : spec,
115 : NULL, NULL))
116 : {
117 0 : GNUNET_break_op (0);
118 0 : return GNUNET_SYSERR;
119 : }
120 : }
121 0 : csrh->cb (csrh->cb_cls,
122 : &csrr);
123 0 : return GNUNET_OK;
124 : }
125 :
126 :
127 : /**
128 : * Function called when we're done processing the HTTP /csr request.
129 : *
130 : * @param cls the `struct TALER_EXCHANGE_CsRMeltHandle`
131 : * @param response_code HTTP response code, 0 on error
132 : * @param response parsed JSON result, NULL on error
133 : */
134 : static void
135 0 : handle_csr_finished (void *cls,
136 : long response_code,
137 : const void *response)
138 : {
139 0 : struct TALER_EXCHANGE_CsRMeltHandle *csrh = cls;
140 0 : const json_t *j = response;
141 0 : struct TALER_EXCHANGE_HttpResponse hr = {
142 : .reply = j,
143 0 : .http_status = (unsigned int) response_code
144 : };
145 0 : struct TALER_EXCHANGE_CsRMeltResponse csrr = {
146 : .hr = hr
147 : };
148 :
149 0 : csrh->job = NULL;
150 0 : switch (response_code)
151 : {
152 0 : case 0:
153 0 : csrr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
154 0 : break;
155 0 : case MHD_HTTP_OK:
156 : {
157 : json_t *arr;
158 :
159 0 : arr = json_object_get (j,
160 : "ewvs");
161 0 : if ( (NULL == arr) ||
162 0 : (0 == json_array_size (arr)) ||
163 : (GNUNET_OK !=
164 0 : csr_ok (csrh,
165 : arr,
166 : &hr)) )
167 : {
168 0 : GNUNET_break_op (0);
169 0 : csrr.hr.http_status = 0;
170 0 : csrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
171 0 : break;
172 : }
173 : }
174 0 : TALER_EXCHANGE_csr_melt_cancel (csrh);
175 0 : return;
176 0 : case MHD_HTTP_BAD_REQUEST:
177 : /* This should never happen, either us or the exchange is buggy
178 : (or API version conflict); just pass JSON reply to the application */
179 0 : csrr.hr.ec = TALER_JSON_get_error_code (j);
180 0 : csrr.hr.hint = TALER_JSON_get_error_hint (j);
181 0 : break;
182 0 : case MHD_HTTP_NOT_FOUND:
183 : /* Nothing really to verify, the exchange basically just says
184 : that it doesn't know the /csr endpoint or denomination.
185 : Can happen if the exchange doesn't support Clause Schnorr.
186 : We should simply pass the JSON reply to the application. */
187 0 : csrr.hr.ec = TALER_JSON_get_error_code (j);
188 0 : csrr.hr.hint = TALER_JSON_get_error_hint (j);
189 0 : break;
190 0 : case MHD_HTTP_GONE:
191 : /* could happen if denomination was revoked */
192 : /* Note: one might want to check /keys for revocation
193 : signature here, alas tricky in case our /keys
194 : is outdated => left to clients */
195 0 : csrr.hr.ec = TALER_JSON_get_error_code (j);
196 0 : csrr.hr.hint = TALER_JSON_get_error_hint (j);
197 0 : break;
198 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
199 : /* Server had an internal issue; we should retry, but this API
200 : leaves this to the application */
201 0 : csrr.hr.ec = TALER_JSON_get_error_code (j);
202 0 : csrr.hr.hint = TALER_JSON_get_error_hint (j);
203 0 : break;
204 0 : default:
205 : /* unexpected response code */
206 0 : GNUNET_break_op (0);
207 0 : csrr.hr.ec = TALER_JSON_get_error_code (j);
208 0 : csrr.hr.hint = TALER_JSON_get_error_hint (j);
209 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
210 : "Unexpected response code %u/%d for CS R request\n",
211 : (unsigned int) response_code,
212 : (int) hr.ec);
213 0 : break;
214 : }
215 0 : csrh->cb (csrh->cb_cls,
216 : &csrr);
217 0 : csrh->cb = NULL;
218 0 : TALER_EXCHANGE_csr_melt_cancel (csrh);
219 : }
220 :
221 :
222 : struct TALER_EXCHANGE_CsRMeltHandle *
223 0 : TALER_EXCHANGE_csr_melt (struct TALER_EXCHANGE_Handle *exchange,
224 : const struct TALER_RefreshMasterSecretP *rms,
225 : unsigned int nks_len,
226 : struct TALER_EXCHANGE_NonceKey *nks,
227 : TALER_EXCHANGE_CsRMeltCallback res_cb,
228 : void *res_cb_cls)
229 : {
230 : struct TALER_EXCHANGE_CsRMeltHandle *csrh;
231 : json_t *csr_arr;
232 :
233 0 : if (0 == nks_len)
234 : {
235 0 : GNUNET_break (0);
236 0 : return NULL;
237 : }
238 0 : for (unsigned int i = 0; i<nks_len; i++)
239 0 : if (TALER_DENOMINATION_CS != nks[i].pk->key.cipher)
240 : {
241 0 : GNUNET_break (0);
242 0 : return NULL;
243 : }
244 0 : csrh = GNUNET_new (struct TALER_EXCHANGE_CsRMeltHandle);
245 0 : csrh->exchange = exchange;
246 0 : csrh->cb = res_cb;
247 0 : csrh->cb_cls = res_cb_cls;
248 0 : csr_arr = json_array ();
249 0 : GNUNET_assert (NULL != csr_arr);
250 0 : for (unsigned int i = 0; i<nks_len; i++)
251 : {
252 0 : const struct TALER_EXCHANGE_NonceKey *nk = &nks[i];
253 : json_t *csr_obj;
254 :
255 0 : csr_obj = GNUNET_JSON_PACK (
256 : GNUNET_JSON_pack_uint64 ("coin_offset",
257 : nk->cnc_num),
258 : GNUNET_JSON_pack_data_auto ("denom_pub_hash",
259 : &nk->pk->h_key));
260 0 : GNUNET_assert (NULL != csr_obj);
261 0 : GNUNET_assert (0 ==
262 : json_array_append_new (csr_arr,
263 : csr_obj));
264 : }
265 0 : csrh->url = TEAH_path_to_url (exchange,
266 : "/csr-melt");
267 0 : if (NULL == csrh->url)
268 : {
269 0 : json_decref (csr_arr);
270 0 : GNUNET_free (csrh);
271 0 : return NULL;
272 : }
273 : {
274 : CURL *eh;
275 : struct GNUNET_CURL_Context *ctx;
276 : json_t *req;
277 :
278 0 : req = GNUNET_JSON_PACK (
279 : GNUNET_JSON_pack_data_auto ("rms",
280 : rms),
281 : GNUNET_JSON_pack_array_steal ("nks",
282 : csr_arr));
283 0 : ctx = TEAH_handle_to_context (exchange);
284 0 : eh = TALER_EXCHANGE_curl_easy_get_ (csrh->url);
285 0 : if ( (NULL == eh) ||
286 : (GNUNET_OK !=
287 0 : TALER_curl_easy_post (&csrh->post_ctx,
288 : eh,
289 : req)) )
290 : {
291 0 : GNUNET_break (0);
292 0 : if (NULL != eh)
293 0 : curl_easy_cleanup (eh);
294 0 : json_decref (req);
295 0 : GNUNET_free (csrh->url);
296 0 : GNUNET_free (csrh);
297 0 : return NULL;
298 : }
299 0 : json_decref (req);
300 0 : csrh->job = GNUNET_CURL_job_add2 (ctx,
301 : eh,
302 0 : csrh->post_ctx.headers,
303 : &handle_csr_finished,
304 : csrh);
305 : }
306 0 : return csrh;
307 : }
308 :
309 :
310 : void
311 0 : TALER_EXCHANGE_csr_melt_cancel (struct TALER_EXCHANGE_CsRMeltHandle *csrh)
312 : {
313 0 : if (NULL != csrh->job)
314 : {
315 0 : GNUNET_CURL_job_cancel (csrh->job);
316 0 : csrh->job = NULL;
317 : }
318 0 : GNUNET_free (csrh->url);
319 0 : TALER_curl_easy_post_finished (&csrh->post_ctx);
320 0 : GNUNET_free (csrh);
321 0 : }
|