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_withdraw.c
19 : * @brief Implementation of /csr-withdraw requests (get R in exchange used for Clause Schnorr withdraw and 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_CsRWithdrawHandle
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_CsRWithdrawCallback 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 av 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_CsRWithdrawHandle *csrh,
90 : const json_t *av,
91 : struct TALER_EXCHANGE_HttpResponse *hr)
92 : {
93 0 : struct TALER_EXCHANGE_CsRWithdrawResponse csrr = {
94 : .hr = *hr,
95 : };
96 : struct GNUNET_JSON_Specification spec[] = {
97 0 : TALER_JSON_spec_exchange_withdraw_values (
98 : "ewv",
99 : &csrr.details.success.alg_values),
100 0 : GNUNET_JSON_spec_end ()
101 : };
102 :
103 0 : if (GNUNET_OK !=
104 0 : GNUNET_JSON_parse (av,
105 : spec,
106 : NULL, NULL))
107 : {
108 0 : GNUNET_break_op (0);
109 0 : return GNUNET_SYSERR;
110 : }
111 0 : csrh->cb (csrh->cb_cls,
112 : &csrr);
113 0 : return GNUNET_OK;
114 : }
115 :
116 :
117 : /**
118 : * Function called when we're done processing the HTTP /csr request.
119 : *
120 : * @param cls the `struct TALER_EXCHANGE_CsRWithdrawHandle`
121 : * @param response_code HTTP response code, 0 on error
122 : * @param response parsed JSON result, NULL on error
123 : */
124 : static void
125 0 : handle_csr_finished (void *cls,
126 : long response_code,
127 : const void *response)
128 : {
129 0 : struct TALER_EXCHANGE_CsRWithdrawHandle *csrh = cls;
130 0 : const json_t *j = response;
131 0 : struct TALER_EXCHANGE_HttpResponse hr = {
132 : .reply = j,
133 0 : .http_status = (unsigned int) response_code
134 : };
135 0 : struct TALER_EXCHANGE_CsRWithdrawResponse csrr = {
136 : .hr = hr
137 : };
138 :
139 0 : csrh->job = NULL;
140 0 : switch (response_code)
141 : {
142 0 : case 0:
143 0 : csrr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
144 0 : break;
145 0 : case MHD_HTTP_OK:
146 : {
147 0 : if (GNUNET_OK !=
148 0 : csr_ok (csrh,
149 : response,
150 : &hr))
151 : {
152 0 : GNUNET_break_op (0);
153 0 : csrr.hr.http_status = 0;
154 0 : csrr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
155 0 : break;
156 : }
157 : }
158 0 : TALER_EXCHANGE_csr_withdraw_cancel (csrh);
159 0 : return;
160 0 : case MHD_HTTP_BAD_REQUEST:
161 : /* This should never happen, either us or the exchange is buggy
162 : (or API version conflict); just pass JSON reply to the application */
163 0 : csrr.hr.ec = TALER_JSON_get_error_code (j);
164 0 : csrr.hr.hint = TALER_JSON_get_error_hint (j);
165 0 : break;
166 0 : case MHD_HTTP_NOT_FOUND:
167 : /* Nothing really to verify, the exchange basically just says
168 : that it doesn't know the /csr endpoint or denomination.
169 : Can happen if the exchange doesn't support Clause Schnorr.
170 : We should simply pass the JSON reply to the application. */
171 0 : csrr.hr.ec = TALER_JSON_get_error_code (j);
172 0 : csrr.hr.hint = TALER_JSON_get_error_hint (j);
173 0 : break;
174 0 : case MHD_HTTP_GONE:
175 : /* could happen if denomination was revoked */
176 : /* Note: one might want to check /keys for revocation
177 : signature here, alas tricky in case our /keys
178 : is outdated => left to clients */
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_INTERNAL_SERVER_ERROR:
183 : /* Server had an internal issue; we should retry, but this API
184 : leaves this to the application */
185 0 : csrr.hr.ec = TALER_JSON_get_error_code (j);
186 0 : csrr.hr.hint = TALER_JSON_get_error_hint (j);
187 0 : break;
188 0 : default:
189 : /* unexpected response code */
190 0 : GNUNET_break_op (0);
191 0 : csrr.hr.ec = TALER_JSON_get_error_code (j);
192 0 : csrr.hr.hint = TALER_JSON_get_error_hint (j);
193 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
194 : "Unexpected response code %u/%d for CS R request\n",
195 : (unsigned int) response_code,
196 : (int) hr.ec);
197 0 : break;
198 : }
199 0 : csrh->cb (csrh->cb_cls,
200 : &csrr);
201 0 : csrh->cb = NULL;
202 0 : TALER_EXCHANGE_csr_withdraw_cancel (csrh);
203 : }
204 :
205 :
206 : struct TALER_EXCHANGE_CsRWithdrawHandle *
207 0 : TALER_EXCHANGE_csr_withdraw (struct TALER_EXCHANGE_Handle *exchange,
208 : const struct TALER_EXCHANGE_DenomPublicKey *pk,
209 : const struct TALER_CsNonce *nonce,
210 : TALER_EXCHANGE_CsRWithdrawCallback res_cb,
211 : void *res_cb_cls)
212 : {
213 : struct TALER_EXCHANGE_CsRWithdrawHandle *csrh;
214 :
215 0 : if (TALER_DENOMINATION_CS != pk->key.cipher)
216 : {
217 0 : GNUNET_break (0);
218 0 : return NULL;
219 : }
220 0 : csrh = GNUNET_new (struct TALER_EXCHANGE_CsRWithdrawHandle);
221 0 : csrh->exchange = exchange;
222 0 : csrh->cb = res_cb;
223 0 : csrh->cb_cls = res_cb_cls;
224 0 : csrh->url = TEAH_path_to_url (exchange,
225 : "/csr-withdraw");
226 0 : if (NULL == csrh->url)
227 : {
228 0 : GNUNET_free (csrh);
229 0 : return NULL;
230 : }
231 :
232 : {
233 : CURL *eh;
234 : struct GNUNET_CURL_Context *ctx;
235 : json_t *req;
236 :
237 0 : req = GNUNET_JSON_PACK (
238 : GNUNET_JSON_pack_data_varsize ("nonce",
239 : nonce,
240 : sizeof(struct TALER_CsNonce)),
241 : GNUNET_JSON_pack_data_varsize ("denom_pub_hash",
242 : &pk->h_key,
243 : sizeof(struct TALER_DenominationHashP)));
244 0 : GNUNET_assert (NULL != req);
245 0 : ctx = TEAH_handle_to_context (exchange);
246 0 : eh = TALER_EXCHANGE_curl_easy_get_ (csrh->url);
247 0 : if ( (NULL == eh) ||
248 : (GNUNET_OK !=
249 0 : TALER_curl_easy_post (&csrh->post_ctx,
250 : eh,
251 : req)) )
252 : {
253 0 : GNUNET_break (0);
254 0 : if (NULL != eh)
255 0 : curl_easy_cleanup (eh);
256 0 : json_decref (req);
257 0 : GNUNET_free (csrh->url);
258 0 : GNUNET_free (csrh);
259 0 : return NULL;
260 : }
261 0 : json_decref (req);
262 0 : csrh->job = GNUNET_CURL_job_add2 (ctx,
263 : eh,
264 0 : csrh->post_ctx.headers,
265 : &handle_csr_finished,
266 : csrh);
267 : }
268 0 : return csrh;
269 : }
270 :
271 :
272 : void
273 0 : TALER_EXCHANGE_csr_withdraw_cancel (struct
274 : TALER_EXCHANGE_CsRWithdrawHandle *csrh)
275 : {
276 0 : if (NULL != csrh->job)
277 : {
278 0 : GNUNET_CURL_job_cancel (csrh->job);
279 0 : csrh->job = NULL;
280 : }
281 0 : GNUNET_free (csrh->url);
282 0 : TALER_curl_easy_post_finished (&csrh->post_ctx);
283 0 : GNUNET_free (csrh);
284 0 : }
|