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