Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2020, 2021 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Affero 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 Affero General Public License for more details.
12 :
13 : You should have received a copy of the GNU Affero General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file taler-exchange-httpd_management_post_keys.c
18 : * @brief Handle request to POST /management/keys
19 : * @author Christian Grothoff
20 : */
21 : #include "platform.h"
22 : #include <gnunet/gnunet_util_lib.h>
23 : #include <gnunet/gnunet_json_lib.h>
24 : #include <jansson.h>
25 : #include <microhttpd.h>
26 : #include <pthread.h>
27 : #include "taler_json_lib.h"
28 : #include "taler_mhd_lib.h"
29 : #include "taler_signatures.h"
30 : #include "taler-exchange-httpd_keys.h"
31 : #include "taler-exchange-httpd_management.h"
32 : #include "taler-exchange-httpd_responses.h"
33 :
34 :
35 : /**
36 : * Denomination signature provided.
37 : */
38 : struct DenomSig
39 : {
40 : /**
41 : * Hash of a denomination public key.
42 : */
43 : struct TALER_DenominationHashP h_denom_pub;
44 :
45 : /**
46 : * Master signature for the @e h_denom_pub.
47 : */
48 : struct TALER_MasterSignatureP master_sig;
49 :
50 : };
51 :
52 :
53 : /**
54 : * Signkey signature provided.
55 : */
56 : struct SigningSig
57 : {
58 : /**
59 : * Online signing key of the exchange.
60 : */
61 : struct TALER_ExchangePublicKeyP exchange_pub;
62 :
63 : /**
64 : * Master signature for the @e exchange_pub.
65 : */
66 : struct TALER_MasterSignatureP master_sig;
67 :
68 : };
69 :
70 :
71 : /**
72 : * Closure for the #add_keys transaction.
73 : */
74 : struct AddKeysContext
75 : {
76 :
77 : /**
78 : * Array of @e nd_sigs denomination signatures.
79 : */
80 : struct DenomSig *d_sigs;
81 :
82 : /**
83 : * Array of @e ns_sigs signkey signatures.
84 : */
85 : struct SigningSig *s_sigs;
86 :
87 : /**
88 : * Our key state.
89 : */
90 : struct TEH_KeyStateHandle *ksh;
91 :
92 : /**
93 : * Length of the d_sigs array.
94 : */
95 : unsigned int nd_sigs;
96 :
97 : /**
98 : * Length of the n_sigs array.
99 : */
100 : unsigned int ns_sigs;
101 :
102 : };
103 :
104 :
105 : /**
106 : * Function implementing database transaction to add offline signing keys.
107 : * Runs the transaction logic; IF it returns a non-error code, the transaction
108 : * logic MUST NOT queue a MHD response. IF it returns an hard error, the
109 : * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it
110 : * returns the soft error code, the function MAY be called again to retry and
111 : * MUST not queue a MHD response.
112 : *
113 : * @param cls closure with a `struct AddKeysContext`
114 : * @param connection MHD request which triggered the transaction
115 : * @param[out] mhd_ret set to MHD response status for @a connection,
116 : * if transaction failed (!)
117 : * @return transaction status
118 : */
119 : static enum GNUNET_DB_QueryStatus
120 0 : add_keys (void *cls,
121 : struct MHD_Connection *connection,
122 : MHD_RESULT *mhd_ret)
123 : {
124 0 : struct AddKeysContext *akc = cls;
125 :
126 : /* activate all denomination keys */
127 0 : for (unsigned int i = 0; i<akc->nd_sigs; i++)
128 : {
129 0 : struct DenomSig *d = &akc->d_sigs[i];
130 : enum GNUNET_DB_QueryStatus qs;
131 0 : bool is_active = false;
132 : struct TALER_EXCHANGEDB_DenominationKeyMetaData meta;
133 : struct TALER_DenominationPublicKey denom_pub;
134 :
135 : /* For idempotency, check if the key is already active */
136 0 : memset (&denom_pub,
137 : 0,
138 : sizeof (denom_pub));
139 0 : qs = TEH_plugin->lookup_denomination_key (
140 0 : TEH_plugin->cls,
141 0 : &d->h_denom_pub,
142 : &meta);
143 0 : if (qs < 0)
144 : {
145 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
146 0 : return qs;
147 0 : GNUNET_break (0);
148 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
149 : MHD_HTTP_INTERNAL_SERVER_ERROR,
150 : TALER_EC_GENERIC_DB_FETCH_FAILED,
151 : "lookup denomination key");
152 0 : return qs;
153 : }
154 0 : if (0 == qs)
155 : {
156 : enum GNUNET_GenericReturnValue rv;
157 :
158 0 : rv = TEH_keys_load_fees (akc->ksh,
159 0 : &d->h_denom_pub,
160 : &denom_pub,
161 : &meta);
162 0 : switch (rv)
163 : {
164 0 : case GNUNET_SYSERR:
165 0 : *mhd_ret = TALER_MHD_reply_with_error (
166 : connection,
167 : MHD_HTTP_INTERNAL_SERVER_ERROR,
168 : TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
169 0 : GNUNET_h2s (&d->h_denom_pub.hash));
170 0 : return GNUNET_DB_STATUS_HARD_ERROR;
171 0 : case GNUNET_NO:
172 0 : *mhd_ret = TALER_MHD_reply_with_error (
173 : connection,
174 : MHD_HTTP_NOT_FOUND,
175 : TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN,
176 0 : GNUNET_h2s (&d->h_denom_pub.hash));
177 0 : return GNUNET_DB_STATUS_HARD_ERROR;
178 0 : case GNUNET_OK:
179 0 : break;
180 : }
181 0 : }
182 : else
183 : {
184 0 : is_active = true;
185 : }
186 :
187 : /* check signature is valid */
188 0 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
189 0 : if (GNUNET_OK !=
190 0 : TALER_exchange_offline_denom_validity_verify (
191 0 : &d->h_denom_pub,
192 : meta.start,
193 : meta.expire_withdraw,
194 : meta.expire_deposit,
195 : meta.expire_legal,
196 : &meta.value,
197 : &meta.fees,
198 : &TEH_master_public_key,
199 0 : &d->master_sig))
200 : {
201 0 : GNUNET_break_op (0);
202 0 : *mhd_ret = TALER_MHD_reply_with_error (
203 : connection,
204 : MHD_HTTP_FORBIDDEN,
205 : TALER_EC_EXCHANGE_MANAGEMENT_KEYS_DENOMKEY_ADD_SIGNATURE_INVALID,
206 0 : GNUNET_h2s (&d->h_denom_pub.hash));
207 0 : if (! is_active)
208 0 : TALER_denom_pub_free (&denom_pub);
209 0 : return GNUNET_DB_STATUS_HARD_ERROR;
210 : }
211 :
212 0 : if (is_active)
213 : {
214 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
215 : "Denomination key %s already active, skipping\n",
216 : GNUNET_h2s (&d->h_denom_pub.hash));
217 0 : continue; /* skip, already known */
218 : }
219 :
220 0 : qs = TEH_plugin->add_denomination_key (
221 0 : TEH_plugin->cls,
222 0 : &d->h_denom_pub,
223 : &denom_pub,
224 : &meta,
225 0 : &d->master_sig);
226 0 : TALER_denom_pub_free (&denom_pub);
227 0 : if (qs < 0)
228 : {
229 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
230 0 : return qs;
231 0 : GNUNET_break (0);
232 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
233 : MHD_HTTP_INTERNAL_SERVER_ERROR,
234 : TALER_EC_GENERIC_DB_STORE_FAILED,
235 : "activate denomination key");
236 0 : return qs;
237 : }
238 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
239 : "Added offline signature for denomination `%s'\n",
240 : GNUNET_h2s (&d->h_denom_pub.hash));
241 0 : GNUNET_assert (0 != qs);
242 : }
243 :
244 0 : for (unsigned int i = 0; i<akc->ns_sigs; i++)
245 : {
246 0 : struct SigningSig *s = &akc->s_sigs[i];
247 : enum GNUNET_DB_QueryStatus qs;
248 0 : bool is_active = false;
249 : struct TALER_EXCHANGEDB_SignkeyMetaData meta;
250 :
251 0 : qs = TEH_plugin->lookup_signing_key (
252 0 : TEH_plugin->cls,
253 0 : &s->exchange_pub,
254 : &meta);
255 0 : if (qs < 0)
256 : {
257 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
258 0 : return qs;
259 0 : GNUNET_break (0);
260 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
261 : MHD_HTTP_INTERNAL_SERVER_ERROR,
262 : TALER_EC_GENERIC_DB_FETCH_FAILED,
263 : "lookup signing key");
264 0 : return qs;
265 : }
266 0 : if (0 == qs)
267 : {
268 0 : if (GNUNET_OK !=
269 0 : TEH_keys_get_timing (&s->exchange_pub,
270 : &meta))
271 : {
272 : /* For idempotency, check if the key is already active */
273 0 : *mhd_ret = TALER_MHD_reply_with_error (
274 : connection,
275 : MHD_HTTP_NOT_FOUND,
276 : TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_UNKNOWN,
277 0 : TALER_B2S (&s->exchange_pub));
278 0 : return GNUNET_DB_STATUS_HARD_ERROR;
279 : }
280 : }
281 : else
282 : {
283 0 : is_active = true; /* if we pass, it's active! */
284 : }
285 :
286 : /* check signature is valid */
287 0 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
288 0 : if (GNUNET_OK !=
289 0 : TALER_exchange_offline_signkey_validity_verify (
290 0 : &s->exchange_pub,
291 : meta.start,
292 : meta.expire_sign,
293 : meta.expire_legal,
294 : &TEH_master_public_key,
295 0 : &s->master_sig))
296 : {
297 0 : GNUNET_break_op (0);
298 0 : *mhd_ret = TALER_MHD_reply_with_error (
299 : connection,
300 : MHD_HTTP_FORBIDDEN,
301 : TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_ADD_SIGNATURE_INVALID,
302 0 : TALER_B2S (&s->exchange_pub));
303 0 : return GNUNET_DB_STATUS_HARD_ERROR;
304 : }
305 0 : if (is_active)
306 : {
307 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
308 : "Signing key %s already active, skipping\n",
309 : TALER_B2S (&s->exchange_pub));
310 0 : continue; /* skip, already known */
311 : }
312 0 : qs = TEH_plugin->activate_signing_key (
313 0 : TEH_plugin->cls,
314 0 : &s->exchange_pub,
315 : &meta,
316 0 : &s->master_sig);
317 0 : if (qs < 0)
318 : {
319 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
320 0 : return qs;
321 0 : GNUNET_break (0);
322 0 : *mhd_ret = TALER_MHD_reply_with_error (connection,
323 : MHD_HTTP_INTERNAL_SERVER_ERROR,
324 : TALER_EC_GENERIC_DB_STORE_FAILED,
325 : "activate signing key");
326 0 : return qs;
327 : }
328 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
329 : "Added offline signature for signing key `%s'\n",
330 : TALER_B2S (&s->exchange_pub));
331 0 : GNUNET_assert (0 != qs);
332 : }
333 0 : return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */
334 : }
335 :
336 :
337 : MHD_RESULT
338 0 : TEH_handler_management_post_keys (
339 : struct MHD_Connection *connection,
340 : const json_t *root)
341 : {
342 : struct AddKeysContext akc;
343 : json_t *denom_sigs;
344 : json_t *signkey_sigs;
345 : struct GNUNET_JSON_Specification spec[] = {
346 0 : GNUNET_JSON_spec_json ("denom_sigs",
347 : &denom_sigs),
348 0 : GNUNET_JSON_spec_json ("signkey_sigs",
349 : &signkey_sigs),
350 0 : GNUNET_JSON_spec_end ()
351 : };
352 : bool ok;
353 : MHD_RESULT ret;
354 :
355 : {
356 : enum GNUNET_GenericReturnValue res;
357 :
358 0 : res = TALER_MHD_parse_json_data (connection,
359 : root,
360 : spec);
361 0 : if (GNUNET_SYSERR == res)
362 0 : return MHD_NO; /* hard failure */
363 0 : if (GNUNET_NO == res)
364 0 : return MHD_YES; /* failure */
365 : }
366 0 : if (! (json_is_array (denom_sigs) &&
367 0 : json_is_array (signkey_sigs)) )
368 : {
369 0 : GNUNET_break_op (0);
370 0 : GNUNET_JSON_parse_free (spec);
371 0 : return TALER_MHD_reply_with_error (
372 : connection,
373 : MHD_HTTP_BAD_REQUEST,
374 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
375 : "array expected for denom_sigs and signkey_sigs");
376 : }
377 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
378 : "Received /management/keys\n");
379 0 : akc.ksh = TEH_keys_get_state2 (true); /* may start its own transaction, thus
380 : must be done here, before we run ours! */
381 0 : if (NULL == akc.ksh)
382 : {
383 0 : GNUNET_break_op (0);
384 0 : GNUNET_JSON_parse_free (spec);
385 0 : return TALER_MHD_reply_with_error (
386 : connection,
387 : MHD_HTTP_INTERNAL_SERVER_ERROR,
388 : TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
389 : "no key state (not even for management)");
390 : }
391 0 : akc.nd_sigs = json_array_size (denom_sigs);
392 0 : akc.d_sigs = GNUNET_new_array (akc.nd_sigs,
393 : struct DenomSig);
394 0 : ok = true;
395 0 : for (unsigned int i = 0; i<akc.nd_sigs; i++)
396 : {
397 0 : struct DenomSig *d = &akc.d_sigs[i];
398 : struct GNUNET_JSON_Specification ispec[] = {
399 0 : GNUNET_JSON_spec_fixed_auto ("master_sig",
400 : &d->master_sig),
401 0 : GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
402 : &d->h_denom_pub),
403 0 : GNUNET_JSON_spec_end ()
404 : };
405 : enum GNUNET_GenericReturnValue res;
406 :
407 0 : res = TALER_MHD_parse_json_data (connection,
408 0 : json_array_get (denom_sigs,
409 : i),
410 : ispec);
411 0 : if (GNUNET_SYSERR == res)
412 : {
413 0 : ret = MHD_NO; /* hard failure */
414 0 : ok = false;
415 0 : break;
416 : }
417 0 : if (GNUNET_NO == res)
418 : {
419 0 : ret = MHD_YES;
420 0 : ok = false;
421 0 : break;
422 : }
423 : }
424 0 : if (! ok)
425 : {
426 0 : GNUNET_free (akc.d_sigs);
427 0 : GNUNET_JSON_parse_free (spec);
428 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
429 : "Failure to handle /management/keys\n");
430 0 : return ret;
431 : }
432 0 : akc.ns_sigs = json_array_size (signkey_sigs);
433 0 : akc.s_sigs = GNUNET_new_array (akc.ns_sigs,
434 : struct SigningSig);
435 0 : for (unsigned int i = 0; i<akc.ns_sigs; i++)
436 : {
437 0 : struct SigningSig *s = &akc.s_sigs[i];
438 : struct GNUNET_JSON_Specification ispec[] = {
439 0 : GNUNET_JSON_spec_fixed_auto ("master_sig",
440 : &s->master_sig),
441 0 : GNUNET_JSON_spec_fixed_auto ("exchange_pub",
442 : &s->exchange_pub),
443 0 : GNUNET_JSON_spec_end ()
444 : };
445 : enum GNUNET_GenericReturnValue res;
446 :
447 0 : res = TALER_MHD_parse_json_data (connection,
448 0 : json_array_get (signkey_sigs,
449 : i),
450 : ispec);
451 0 : if (GNUNET_SYSERR == res)
452 : {
453 0 : ret = MHD_NO; /* hard failure */
454 0 : ok = false;
455 0 : break;
456 : }
457 0 : if (GNUNET_NO == res)
458 : {
459 0 : ret = MHD_YES;
460 0 : ok = false;
461 0 : break;
462 : }
463 : }
464 0 : if (! ok)
465 : {
466 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
467 : "Failure to handle /management/keys\n");
468 0 : GNUNET_free (akc.d_sigs);
469 0 : GNUNET_free (akc.s_sigs);
470 0 : GNUNET_JSON_parse_free (spec);
471 0 : return ret;
472 : }
473 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
474 : "Received %u denomination and %u signing key signatures\n",
475 : akc.nd_sigs,
476 : akc.ns_sigs);
477 : {
478 : enum GNUNET_GenericReturnValue res;
479 :
480 0 : res = TEH_DB_run_transaction (connection,
481 : "add keys",
482 : TEH_MT_REQUEST_OTHER,
483 : &ret,
484 : &add_keys,
485 : &akc);
486 0 : GNUNET_free (akc.d_sigs);
487 0 : GNUNET_free (akc.s_sigs);
488 0 : GNUNET_JSON_parse_free (spec);
489 0 : if (GNUNET_SYSERR == res)
490 0 : return ret;
491 : }
492 0 : TEH_keys_update_states ();
493 0 : return TALER_MHD_reply_static (
494 : connection,
495 : MHD_HTTP_NO_CONTENT,
496 : NULL,
497 : NULL,
498 : 0);
499 : }
500 :
501 :
502 : /* end of taler-exchange-httpd_management_management_post_keys.c */
|