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