Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2015-2023 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_management_get_keys.c
19 : * @brief functions to obtain future online keys of the exchange
20 : * @author Christian Grothoff
21 : */
22 : #include "taler/platform.h"
23 : #include "taler/taler_json_lib.h"
24 : #include <gnunet/gnunet_curl_lib.h>
25 : #include <microhttpd.h>
26 : #include "taler/taler_exchange_service.h"
27 : #include "exchange_api_curl_defaults.h"
28 : #include "taler/taler_signatures.h"
29 : #include "taler/taler_curl_lib.h"
30 : #include "taler/taler_util.h"
31 : #include "taler/taler_json_lib.h"
32 :
33 : /**
34 : * Set to 1 for extra debug logging.
35 : */
36 : #define DEBUG 0
37 :
38 :
39 : /**
40 : * @brief Handle for a GET /management/keys request.
41 : */
42 : struct TALER_EXCHANGE_ManagementGetKeysHandle
43 : {
44 :
45 : /**
46 : * The url for this request.
47 : */
48 : char *url;
49 :
50 : /**
51 : * Handle for the request.
52 : */
53 : struct GNUNET_CURL_Job *job;
54 :
55 : /**
56 : * Function to call with the result.
57 : */
58 : TALER_EXCHANGE_ManagementGetKeysCallback cb;
59 :
60 : /**
61 : * Closure for @a cb.
62 : */
63 : void *cb_cls;
64 :
65 : /**
66 : * Reference to the execution context.
67 : */
68 : struct GNUNET_CURL_Context *ctx;
69 : };
70 :
71 :
72 : /**
73 : * Handle the case that the response was of type #MHD_HTTP_OK.
74 : *
75 : * @param[in,out] gh request handle
76 : * @param response the response
77 : * @return #GNUNET_OK if the response was well-formed
78 : */
79 : static enum GNUNET_GenericReturnValue
80 19 : handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
81 : const json_t *response)
82 : {
83 19 : struct TALER_EXCHANGE_ManagementGetKeysResponse gkr = {
84 : .hr.http_status = MHD_HTTP_OK,
85 : .hr.reply = response,
86 : };
87 19 : struct TALER_EXCHANGE_FutureKeys *fk
88 : = &gkr.details.ok.keys;
89 : const json_t *sk;
90 : const json_t *dk;
91 : bool ok;
92 : struct GNUNET_JSON_Specification spec[] = {
93 19 : GNUNET_JSON_spec_array_const ("future_denoms",
94 : &dk),
95 19 : GNUNET_JSON_spec_array_const ("future_signkeys",
96 : &sk),
97 19 : GNUNET_JSON_spec_fixed_auto ("master_pub",
98 : &fk->master_pub),
99 19 : GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
100 : &fk->denom_secmod_public_key),
101 19 : GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
102 : &fk->denom_secmod_cs_public_key),
103 19 : GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
104 : &fk->signkey_secmod_public_key),
105 19 : GNUNET_JSON_spec_end ()
106 : };
107 :
108 19 : if (GNUNET_OK !=
109 19 : GNUNET_JSON_parse (response,
110 : spec,
111 : NULL, NULL))
112 : {
113 0 : GNUNET_break_op (0);
114 0 : return GNUNET_SYSERR;
115 : }
116 19 : fk->num_sign_keys = json_array_size (sk);
117 19 : fk->num_denom_keys = json_array_size (dk);
118 19 : fk->sign_keys = GNUNET_new_array (
119 : fk->num_sign_keys,
120 : struct TALER_EXCHANGE_FutureSigningPublicKey);
121 19 : fk->denom_keys = GNUNET_new_array (
122 : fk->num_denom_keys,
123 : struct TALER_EXCHANGE_FutureDenomPublicKey);
124 19 : ok = true;
125 76 : for (unsigned int i = 0; i<fk->num_sign_keys; i++)
126 : {
127 57 : json_t *j = json_array_get (sk,
128 : i);
129 57 : struct TALER_EXCHANGE_FutureSigningPublicKey *sign_key
130 57 : = &fk->sign_keys[i];
131 : struct GNUNET_JSON_Specification ispec[] = {
132 57 : GNUNET_JSON_spec_fixed_auto ("key",
133 : &sign_key->key),
134 57 : GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig",
135 : &sign_key->signkey_secmod_sig),
136 57 : GNUNET_JSON_spec_timestamp ("stamp_start",
137 : &sign_key->valid_from),
138 57 : GNUNET_JSON_spec_timestamp ("stamp_expire",
139 : &sign_key->valid_until),
140 57 : GNUNET_JSON_spec_timestamp ("stamp_end",
141 : &sign_key->valid_legal),
142 57 : GNUNET_JSON_spec_end ()
143 : };
144 :
145 57 : if (GNUNET_OK !=
146 57 : GNUNET_JSON_parse (j,
147 : ispec,
148 : NULL, NULL))
149 : {
150 0 : GNUNET_break_op (0);
151 0 : ok = false;
152 0 : break;
153 : }
154 : {
155 : struct GNUNET_TIME_Relative duration
156 57 : = GNUNET_TIME_absolute_get_difference (sign_key->valid_from.abs_time,
157 : sign_key->valid_until.abs_time);
158 :
159 57 : if (GNUNET_OK !=
160 57 : TALER_exchange_secmod_eddsa_verify (
161 57 : &sign_key->key,
162 : sign_key->valid_from,
163 : duration,
164 57 : &fk->signkey_secmod_public_key,
165 57 : &sign_key->signkey_secmod_sig))
166 : {
167 0 : GNUNET_break_op (0);
168 0 : ok = false;
169 0 : break;
170 : }
171 : }
172 : }
173 5392 : for (unsigned int i = 0; i<fk->num_denom_keys; i++)
174 : {
175 5373 : json_t *j = json_array_get (dk,
176 : i);
177 5373 : struct TALER_EXCHANGE_FutureDenomPublicKey *denom_key
178 5373 : = &fk->denom_keys[i];
179 : const char *section_name;
180 : struct GNUNET_JSON_Specification ispec[] = {
181 5373 : TALER_JSON_spec_amount_any ("value",
182 : &denom_key->value),
183 5373 : GNUNET_JSON_spec_timestamp ("stamp_start",
184 : &denom_key->valid_from),
185 5373 : GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
186 : &denom_key->withdraw_valid_until),
187 5373 : GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
188 : &denom_key->expire_deposit),
189 5373 : GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
190 : &denom_key->expire_legal),
191 5373 : TALER_JSON_spec_denom_pub ("denom_pub",
192 : &denom_key->key),
193 5373 : TALER_JSON_spec_amount_any ("fee_withdraw",
194 : &denom_key->fee_withdraw),
195 5373 : TALER_JSON_spec_amount_any ("fee_deposit",
196 : &denom_key->fee_deposit),
197 5373 : TALER_JSON_spec_amount_any ("fee_refresh",
198 : &denom_key->fee_refresh),
199 5373 : TALER_JSON_spec_amount_any ("fee_refund",
200 : &denom_key->fee_refund),
201 5373 : GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig",
202 : &denom_key->denom_secmod_sig),
203 5373 : GNUNET_JSON_spec_string ("section_name",
204 : §ion_name),
205 5373 : GNUNET_JSON_spec_end ()
206 : };
207 :
208 5373 : if (GNUNET_OK !=
209 5373 : GNUNET_JSON_parse (j,
210 : ispec,
211 : NULL, NULL))
212 : {
213 0 : GNUNET_break_op (0);
214 : #if DEBUG
215 : json_dumpf (j,
216 : stderr,
217 : JSON_INDENT (2));
218 : #endif
219 0 : ok = false;
220 0 : break;
221 : }
222 :
223 : {
224 : struct TALER_DenominationHashP h_denom_pub;
225 : struct GNUNET_TIME_Relative duration
226 5373 : = GNUNET_TIME_absolute_get_difference (
227 : denom_key->valid_from.abs_time,
228 : denom_key->withdraw_valid_until.abs_time);
229 :
230 5373 : TALER_denom_pub_hash (&denom_key->key,
231 : &h_denom_pub);
232 5373 : switch (denom_key->key.bsign_pub_key->cipher)
233 : {
234 2710 : case GNUNET_CRYPTO_BSA_RSA:
235 : {
236 : struct TALER_RsaPubHashP h_rsa;
237 :
238 2710 : TALER_rsa_pub_hash (
239 2710 : denom_key->key.bsign_pub_key->details.rsa_public_key,
240 : &h_rsa);
241 2710 : if (GNUNET_OK !=
242 2710 : TALER_exchange_secmod_rsa_verify (&h_rsa,
243 : section_name,
244 : denom_key->valid_from,
245 : duration,
246 2710 : &fk->denom_secmod_public_key,
247 2710 : &denom_key->denom_secmod_sig))
248 : {
249 0 : GNUNET_break_op (0);
250 0 : ok = false;
251 0 : break;
252 : }
253 : }
254 2710 : break;
255 2663 : case GNUNET_CRYPTO_BSA_CS:
256 : {
257 : struct TALER_CsPubHashP h_cs;
258 :
259 2663 : TALER_cs_pub_hash (
260 2663 : &denom_key->key.bsign_pub_key->details.cs_public_key,
261 : &h_cs);
262 2663 : if (GNUNET_OK !=
263 2663 : TALER_exchange_secmod_cs_verify (&h_cs,
264 : section_name,
265 : denom_key->valid_from,
266 : duration,
267 2663 : &fk->denom_secmod_cs_public_key,
268 2663 : &denom_key->denom_secmod_sig))
269 : {
270 0 : GNUNET_break_op (0);
271 0 : ok = false;
272 0 : break;
273 : }
274 : }
275 2663 : break;
276 0 : default:
277 0 : GNUNET_break_op (0);
278 0 : ok = false;
279 0 : break;
280 : }
281 : }
282 5373 : if (! ok)
283 0 : break;
284 : }
285 19 : if (ok)
286 : {
287 19 : gh->cb (gh->cb_cls,
288 : &gkr);
289 : }
290 5392 : for (unsigned int i = 0; i<fk->num_denom_keys; i++)
291 5373 : TALER_denom_pub_free (&fk->denom_keys[i].key);
292 19 : GNUNET_free (fk->sign_keys);
293 19 : GNUNET_free (fk->denom_keys);
294 19 : return (ok) ? GNUNET_OK : GNUNET_SYSERR;
295 : }
296 :
297 :
298 : /**
299 : * Function called when we're done processing the
300 : * HTTP GET /management/keys request.
301 : *
302 : * @param cls the `struct TALER_EXCHANGE_ManagementGetKeysHandle *`
303 : * @param response_code HTTP response code, 0 on error
304 : * @param response response body, NULL if not in JSON
305 : */
306 : static void
307 19 : handle_get_keys_finished (void *cls,
308 : long response_code,
309 : const void *response)
310 : {
311 19 : struct TALER_EXCHANGE_ManagementGetKeysHandle *gh = cls;
312 19 : const json_t *json = response;
313 19 : struct TALER_EXCHANGE_ManagementGetKeysResponse gkr = {
314 19 : .hr.http_status = (unsigned int) response_code,
315 : .hr.reply = json
316 : };
317 :
318 19 : gh->job = NULL;
319 19 : switch (response_code)
320 : {
321 19 : case MHD_HTTP_OK:
322 19 : if (GNUNET_OK ==
323 19 : handle_ok (gh,
324 : response))
325 : {
326 19 : gh->cb = NULL;
327 : }
328 : else
329 : {
330 0 : response_code = 0;
331 : }
332 19 : break;
333 0 : case MHD_HTTP_NOT_FOUND:
334 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
335 : "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n",
336 : gh->url);
337 0 : if (NULL != json)
338 : {
339 0 : gkr.hr.ec = TALER_JSON_get_error_code (json);
340 0 : gkr.hr.hint = TALER_JSON_get_error_hint (json);
341 : }
342 : else
343 : {
344 0 : gkr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
345 0 : gkr.hr.hint = TALER_ErrorCode_get_hint (gkr.hr.ec);
346 : }
347 0 : break;
348 0 : default:
349 : /* unexpected response code */
350 0 : if (NULL != json)
351 : {
352 0 : gkr.hr.ec = TALER_JSON_get_error_code (json);
353 0 : gkr.hr.hint = TALER_JSON_get_error_hint (json);
354 : }
355 : else
356 : {
357 0 : gkr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
358 0 : gkr.hr.hint = TALER_ErrorCode_get_hint (gkr.hr.ec);
359 : }
360 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
361 : "Unexpected response code %u/%d for exchange management get keys\n",
362 : (unsigned int) response_code,
363 : (int) gkr.hr.ec);
364 0 : break;
365 : }
366 19 : if (NULL != gh->cb)
367 : {
368 0 : gh->cb (gh->cb_cls,
369 : &gkr);
370 0 : gh->cb = NULL;
371 : }
372 19 : TALER_EXCHANGE_get_management_keys_cancel (gh);
373 19 : };
374 :
375 :
376 : struct TALER_EXCHANGE_ManagementGetKeysHandle *
377 19 : TALER_EXCHANGE_get_management_keys (struct GNUNET_CURL_Context *ctx,
378 : const char *url,
379 : TALER_EXCHANGE_ManagementGetKeysCallback cb,
380 : void *cb_cls)
381 : {
382 : struct TALER_EXCHANGE_ManagementGetKeysHandle *gh;
383 : CURL *eh;
384 :
385 19 : gh = GNUNET_new (struct TALER_EXCHANGE_ManagementGetKeysHandle);
386 19 : gh->cb = cb;
387 19 : gh->cb_cls = cb_cls;
388 19 : gh->ctx = ctx;
389 19 : gh->url = TALER_url_join (url,
390 : "management/keys",
391 : NULL);
392 19 : if (NULL == gh->url)
393 : {
394 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
395 : "Could not construct request URL.\n");
396 0 : GNUNET_free (gh);
397 0 : return NULL;
398 : }
399 19 : eh = TALER_EXCHANGE_curl_easy_get_ (gh->url);
400 19 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
401 : "Requesting URL '%s'\n",
402 : gh->url);
403 19 : gh->job = GNUNET_CURL_job_add (ctx,
404 : eh,
405 : &handle_get_keys_finished,
406 : gh);
407 19 : if (NULL == gh->job)
408 : {
409 0 : TALER_EXCHANGE_get_management_keys_cancel (gh);
410 0 : return NULL;
411 : }
412 19 : return gh;
413 : }
414 :
415 :
416 : void
417 19 : TALER_EXCHANGE_get_management_keys_cancel (
418 : struct TALER_EXCHANGE_ManagementGetKeysHandle *gh)
419 : {
420 19 : if (NULL != gh->job)
421 : {
422 0 : GNUNET_CURL_job_cancel (gh->job);
423 0 : gh->job = NULL;
424 : }
425 19 : GNUNET_free (gh->url);
426 19 : GNUNET_free (gh);
427 19 : }
|