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 "platform.h"
23 : #include "taler_json_lib.h"
24 : #include <gnunet/gnunet_curl_lib.h>
25 : #include <microhttpd.h>
26 : #include "taler_exchange_service.h"
27 : #include "exchange_api_curl_defaults.h"
28 : #include "taler_signatures.h"
29 : #include "taler_curl_lib.h"
30 : #include "taler_util.h"
31 : #include "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 21 : handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
81 : const json_t *response)
82 : {
83 21 : struct TALER_EXCHANGE_ManagementGetKeysResponse gkr = {
84 : .hr.http_status = MHD_HTTP_OK,
85 : .hr.reply = response,
86 : };
87 21 : 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 21 : GNUNET_JSON_spec_array_const ("future_denoms",
94 : &dk),
95 21 : GNUNET_JSON_spec_array_const ("future_signkeys",
96 : &sk),
97 21 : GNUNET_JSON_spec_fixed_auto ("master_pub",
98 : &fk->master_pub),
99 21 : GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
100 : &fk->denom_secmod_public_key),
101 21 : GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
102 : &fk->denom_secmod_cs_public_key),
103 21 : GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
104 : &fk->signkey_secmod_public_key),
105 21 : GNUNET_JSON_spec_end ()
106 : };
107 :
108 21 : if (GNUNET_OK !=
109 21 : GNUNET_JSON_parse (response,
110 : spec,
111 : NULL, NULL))
112 : {
113 0 : GNUNET_break_op (0);
114 0 : return GNUNET_SYSERR;
115 : }
116 21 : fk->num_sign_keys = json_array_size (sk);
117 21 : fk->num_denom_keys = json_array_size (dk);
118 21 : fk->sign_keys = GNUNET_new_array (
119 : fk->num_sign_keys,
120 : struct TALER_EXCHANGE_FutureSigningPublicKey);
121 21 : fk->denom_keys = GNUNET_new_array (
122 : fk->num_denom_keys,
123 : struct TALER_EXCHANGE_FutureDenomPublicKey);
124 21 : ok = true;
125 88 : for (unsigned int i = 0; i<fk->num_sign_keys; i++)
126 : {
127 67 : json_t *j = json_array_get (sk,
128 : i);
129 67 : struct TALER_EXCHANGE_FutureSigningPublicKey *sign_key
130 67 : = &fk->sign_keys[i];
131 : struct GNUNET_JSON_Specification ispec[] = {
132 67 : GNUNET_JSON_spec_fixed_auto ("key",
133 : &sign_key->key),
134 67 : GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig",
135 : &sign_key->signkey_secmod_sig),
136 67 : GNUNET_JSON_spec_timestamp ("stamp_start",
137 : &sign_key->valid_from),
138 67 : GNUNET_JSON_spec_timestamp ("stamp_expire",
139 : &sign_key->valid_until),
140 67 : GNUNET_JSON_spec_timestamp ("stamp_end",
141 : &sign_key->valid_legal),
142 67 : GNUNET_JSON_spec_end ()
143 : };
144 :
145 67 : if (GNUNET_OK !=
146 67 : 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 67 : = GNUNET_TIME_absolute_get_difference (sign_key->valid_from.abs_time,
157 : sign_key->valid_until.abs_time);
158 :
159 67 : if (GNUNET_OK !=
160 67 : TALER_exchange_secmod_eddsa_verify (
161 67 : &sign_key->key,
162 : sign_key->valid_from,
163 : duration,
164 67 : &fk->signkey_secmod_public_key,
165 67 : &sign_key->signkey_secmod_sig))
166 : {
167 0 : GNUNET_break_op (0);
168 0 : ok = false;
169 0 : break;
170 : }
171 : }
172 : }
173 6241 : for (unsigned int i = 0; i<fk->num_denom_keys; i++)
174 : {
175 6220 : json_t *j = json_array_get (dk,
176 : i);
177 6220 : struct TALER_EXCHANGE_FutureDenomPublicKey *denom_key
178 6220 : = &fk->denom_keys[i];
179 : const char *section_name;
180 : struct GNUNET_JSON_Specification ispec[] = {
181 6220 : TALER_JSON_spec_amount_any ("value",
182 : &denom_key->value),
183 6220 : GNUNET_JSON_spec_timestamp ("stamp_start",
184 : &denom_key->valid_from),
185 6220 : GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
186 : &denom_key->withdraw_valid_until),
187 6220 : GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
188 : &denom_key->expire_deposit),
189 6220 : GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
190 : &denom_key->expire_legal),
191 6220 : TALER_JSON_spec_denom_pub ("denom_pub",
192 : &denom_key->key),
193 6220 : TALER_JSON_spec_amount_any ("fee_withdraw",
194 : &denom_key->fee_withdraw),
195 6220 : TALER_JSON_spec_amount_any ("fee_deposit",
196 : &denom_key->fee_deposit),
197 6220 : TALER_JSON_spec_amount_any ("fee_refresh",
198 : &denom_key->fee_refresh),
199 6220 : TALER_JSON_spec_amount_any ("fee_refund",
200 : &denom_key->fee_refund),
201 6220 : GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig",
202 : &denom_key->denom_secmod_sig),
203 6220 : GNUNET_JSON_spec_string ("section_name",
204 : §ion_name),
205 6220 : GNUNET_JSON_spec_end ()
206 : };
207 :
208 6220 : if (GNUNET_OK !=
209 6220 : 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 6220 : = GNUNET_TIME_absolute_get_difference (
227 : denom_key->valid_from.abs_time,
228 : denom_key->withdraw_valid_until.abs_time);
229 :
230 6220 : TALER_denom_pub_hash (&denom_key->key,
231 : &h_denom_pub);
232 6220 : switch (denom_key->key.bsign_pub_key->cipher)
233 : {
234 3558 : case GNUNET_CRYPTO_BSA_RSA:
235 : {
236 : struct TALER_RsaPubHashP h_rsa;
237 :
238 3558 : TALER_rsa_pub_hash (
239 3558 : denom_key->key.bsign_pub_key->details.rsa_public_key,
240 : &h_rsa);
241 3558 : if (GNUNET_OK !=
242 3558 : TALER_exchange_secmod_rsa_verify (&h_rsa,
243 : section_name,
244 : denom_key->valid_from,
245 : duration,
246 3558 : &fk->denom_secmod_public_key,
247 3558 : &denom_key->denom_secmod_sig))
248 : {
249 0 : GNUNET_break_op (0);
250 0 : ok = false;
251 0 : break;
252 : }
253 : }
254 3558 : break;
255 2662 : case GNUNET_CRYPTO_BSA_CS:
256 : {
257 : struct TALER_CsPubHashP h_cs;
258 :
259 2662 : TALER_cs_pub_hash (
260 2662 : &denom_key->key.bsign_pub_key->details.cs_public_key,
261 : &h_cs);
262 2662 : if (GNUNET_OK !=
263 2662 : TALER_exchange_secmod_cs_verify (&h_cs,
264 : section_name,
265 : denom_key->valid_from,
266 : duration,
267 2662 : &fk->denom_secmod_cs_public_key,
268 2662 : &denom_key->denom_secmod_sig))
269 : {
270 0 : GNUNET_break_op (0);
271 0 : ok = false;
272 0 : break;
273 : }
274 : }
275 2662 : break;
276 0 : default:
277 0 : GNUNET_break_op (0);
278 0 : ok = false;
279 0 : break;
280 : }
281 : }
282 6220 : if (! ok)
283 0 : break;
284 : }
285 21 : if (ok)
286 : {
287 21 : gh->cb (gh->cb_cls,
288 : &gkr);
289 : }
290 6241 : for (unsigned int i = 0; i<fk->num_denom_keys; i++)
291 6220 : TALER_denom_pub_free (&fk->denom_keys[i].key);
292 21 : GNUNET_free (fk->sign_keys);
293 21 : GNUNET_free (fk->denom_keys);
294 21 : 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 21 : handle_get_keys_finished (void *cls,
308 : long response_code,
309 : const void *response)
310 : {
311 21 : struct TALER_EXCHANGE_ManagementGetKeysHandle *gh = cls;
312 21 : const json_t *json = response;
313 21 : struct TALER_EXCHANGE_ManagementGetKeysResponse gkr = {
314 21 : .hr.http_status = (unsigned int) response_code,
315 : .hr.reply = json
316 : };
317 :
318 21 : gh->job = NULL;
319 21 : switch (response_code)
320 : {
321 21 : case MHD_HTTP_OK:
322 21 : if (GNUNET_OK ==
323 21 : handle_ok (gh,
324 : response))
325 : {
326 21 : gh->cb = NULL;
327 : }
328 : else
329 : {
330 0 : response_code = 0;
331 : }
332 21 : 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 21 : if (NULL != gh->cb)
367 : {
368 0 : gh->cb (gh->cb_cls,
369 : &gkr);
370 0 : gh->cb = NULL;
371 : }
372 21 : TALER_EXCHANGE_get_management_keys_cancel (gh);
373 21 : };
374 :
375 :
376 : struct TALER_EXCHANGE_ManagementGetKeysHandle *
377 21 : 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 21 : gh = GNUNET_new (struct TALER_EXCHANGE_ManagementGetKeysHandle);
386 21 : gh->cb = cb;
387 21 : gh->cb_cls = cb_cls;
388 21 : gh->ctx = ctx;
389 21 : gh->url = TALER_url_join (url,
390 : "management/keys",
391 : NULL);
392 21 : 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 21 : eh = TALER_EXCHANGE_curl_easy_get_ (gh->url);
400 21 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
401 : "Requesting URL '%s'\n",
402 : gh->url);
403 21 : gh->job = GNUNET_CURL_job_add (ctx,
404 : eh,
405 : &handle_get_keys_finished,
406 : gh);
407 21 : if (NULL == gh->job)
408 : {
409 0 : TALER_EXCHANGE_get_management_keys_cancel (gh);
410 0 : return NULL;
411 : }
412 21 : return gh;
413 : }
414 :
415 :
416 : void
417 21 : TALER_EXCHANGE_get_management_keys_cancel (
418 : struct TALER_EXCHANGE_ManagementGetKeysHandle *gh)
419 : {
420 21 : if (NULL != gh->job)
421 : {
422 0 : GNUNET_CURL_job_cancel (gh->job);
423 0 : gh->job = NULL;
424 : }
425 21 : GNUNET_free (gh->url);
426 21 : GNUNET_free (gh);
427 21 : }
|