Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2020-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 <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file taler-auditor-offline.c
18 : * @brief Support for operations involving the auditor's (offline) key.
19 : * @author Christian Grothoff
20 : */
21 : #include "platform.h"
22 : #include <gnunet/gnunet_json_lib.h>
23 : #include <microhttpd.h>
24 : #include "taler/taler_json_lib.h"
25 :
26 : #define TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE \
27 : char *const
28 : #include "taler/exchange/get-keys.h"
29 :
30 : struct DenominationAddRequest;
31 : #define TALER_EXCHANGE_POST_AUDITORS_RESULT_CLOSURE \
32 : struct DenominationAddRequest
33 : #include "taler/exchange/post-auditors-AUDITOR_PUB-H_DENOM_PUB.h"
34 :
35 : /**
36 : * Name of the input of a denomination key signature for the 'upload' operation.
37 : * The "auditor-" prefix ensures that there is no ambiguity between
38 : * taler-exchange-offline and taler-auditor-offline JSON formats.
39 : * The last component --by convention-- identifies the protocol version
40 : * and should be incremented whenever the JSON format of the 'argument' changes.
41 : */
42 : #define OP_SIGN_DENOMINATION "auditor-sign-denomination-0"
43 :
44 : /**
45 : * Name of the input for the 'sign' and 'show' operations.
46 : * The "auditor-" prefix ensures that there is no ambiguity between
47 : * taler-exchange-offline and taler-auditor-offline JSON formats.
48 : * The last component --by convention-- identifies the protocol version
49 : * and should be incremented whenever the JSON format of the 'argument' changes.
50 : */
51 : #define OP_INPUT_KEYS "auditor-keys-0"
52 :
53 : /**
54 : * Setup the offline signing key.
55 : * The last component --by convention-- identifies the protocol version
56 : * and should be incremented whenever the JSON format of the 'argument' changes.
57 : */
58 : #define OP_SETUP "auditor-setup-0"
59 :
60 : /**
61 : * Our private key, initialized in #load_offline_key().
62 : */
63 : static struct TALER_AuditorPrivateKeyP auditor_priv;
64 :
65 : /**
66 : * Our private key, initialized in #load_offline_key().
67 : */
68 : static struct TALER_AuditorPublicKeyP auditor_pub;
69 :
70 : /**
71 : * Base URL of this auditor's REST endpoint.
72 : */
73 : static char *auditor_url;
74 :
75 : /**
76 : * Exchange's master public key.
77 : */
78 : static struct TALER_MasterPublicKeyP master_pub;
79 :
80 : /**
81 : * Our context for making HTTP requests.
82 : */
83 : static struct GNUNET_CURL_Context *ctx;
84 :
85 : /**
86 : * Reschedule context for #ctx.
87 : */
88 : static struct GNUNET_CURL_RescheduleContext *rc;
89 :
90 : /**
91 : * Handle to the exchange's configuration
92 : */
93 : static const struct GNUNET_CONFIGURATION_Handle *kcfg;
94 :
95 : /**
96 : * Return value from main().
97 : */
98 : static int global_ret;
99 :
100 : /**
101 : * Input to consume.
102 : */
103 : static json_t *in;
104 :
105 : /**
106 : * Array of actions to perform.
107 : */
108 : static json_t *out;
109 :
110 : /**
111 : * Currency supported by this auditor.
112 : */
113 : static char *currency;
114 :
115 :
116 : /**
117 : * A subcommand supported by this program.
118 : */
119 : struct SubCommand
120 : {
121 : /**
122 : * Name of the command.
123 : */
124 : const char *name;
125 :
126 : /**
127 : * Help text for the command.
128 : */
129 : const char *help;
130 :
131 : /**
132 : * Function implementing the command.
133 : *
134 : * @param args subsequent command line arguments (char **)
135 : */
136 : void (*cb)(char *const *args);
137 : };
138 :
139 :
140 : /**
141 : * Data structure for denomination add requests.
142 : */
143 : struct DenominationAddRequest
144 : {
145 :
146 : /**
147 : * Kept in a DLL.
148 : */
149 : struct DenominationAddRequest *next;
150 :
151 : /**
152 : * Kept in a DLL.
153 : */
154 : struct DenominationAddRequest *prev;
155 :
156 : /**
157 : * Operation handle.
158 : */
159 : struct TALER_EXCHANGE_PostAuditorsHandle *h;
160 :
161 : /**
162 : * Array index of the associated command.
163 : */
164 : size_t idx;
165 : };
166 :
167 :
168 : /**
169 : * Next work item to perform.
170 : */
171 : static struct GNUNET_SCHEDULER_Task *nxt;
172 :
173 : /**
174 : * Active denomination add requests.
175 : */
176 : static struct DenominationAddRequest *dar_head;
177 :
178 : /**
179 : * Active denomination add requests.
180 : */
181 : static struct DenominationAddRequest *dar_tail;
182 :
183 : /**
184 : * Handle to the exchange, used to request /keys.
185 : */
186 : static struct TALER_EXCHANGE_GetKeysHandle *exchange;
187 :
188 :
189 : /**
190 : * Shutdown task. Invoked when the application is being terminated.
191 : *
192 : * @param cls NULL
193 : */
194 : static void
195 6 : do_shutdown (void *cls)
196 : {
197 : (void) cls;
198 :
199 : {
200 : struct DenominationAddRequest *dar;
201 :
202 6 : while (NULL != (dar = dar_head))
203 : {
204 0 : fprintf (stderr,
205 : "Aborting incomplete wire add #%u\n",
206 0 : (unsigned int) dar->idx);
207 0 : TALER_EXCHANGE_post_auditors_cancel (dar->h);
208 0 : GNUNET_CONTAINER_DLL_remove (dar_head,
209 : dar_tail,
210 : dar);
211 0 : GNUNET_free (dar);
212 : }
213 : }
214 6 : if (NULL != out)
215 : {
216 0 : json_dumpf (out,
217 : stdout,
218 : JSON_INDENT (2));
219 0 : json_decref (out);
220 0 : out = NULL;
221 : }
222 6 : if (NULL != in)
223 : {
224 0 : fprintf (stderr,
225 : "Warning: input not consumed!\n");
226 0 : json_decref (in);
227 0 : in = NULL;
228 : }
229 6 : if (NULL != exchange)
230 : {
231 0 : TALER_EXCHANGE_get_keys_cancel (exchange);
232 0 : exchange = NULL;
233 : }
234 6 : if (NULL != nxt)
235 : {
236 0 : GNUNET_SCHEDULER_cancel (nxt);
237 0 : nxt = NULL;
238 : }
239 6 : if (NULL != ctx)
240 : {
241 6 : GNUNET_CURL_fini (ctx);
242 6 : ctx = NULL;
243 : }
244 6 : if (NULL != rc)
245 : {
246 6 : GNUNET_CURL_gnunet_rc_destroy (rc);
247 6 : rc = NULL;
248 : }
249 6 : }
250 :
251 :
252 : /**
253 : * Test if we should shut down because all tasks are done.
254 : */
255 : static void
256 186 : test_shutdown (void)
257 : {
258 186 : if ( (NULL == dar_head) &&
259 6 : (NULL == exchange) &&
260 6 : (NULL == nxt) )
261 6 : GNUNET_SCHEDULER_shutdown ();
262 186 : }
263 :
264 :
265 : /**
266 : * Function to continue processing the next command.
267 : *
268 : * @param cls must be a `char *const*` with the array of
269 : * command-line arguments to process next
270 : */
271 : static void
272 : work (void *cls);
273 :
274 :
275 : /**
276 : * Function to schedule job to process the next command.
277 : *
278 : * @param args the array of command-line arguments to process next
279 : */
280 : static void
281 18 : next (char *const *args)
282 : {
283 18 : GNUNET_assert (NULL == nxt);
284 18 : if (NULL == args[0])
285 : {
286 0 : test_shutdown ();
287 0 : return;
288 : }
289 18 : nxt = GNUNET_SCHEDULER_add_now (&work,
290 : (void *) args);
291 : }
292 :
293 :
294 : /**
295 : * Add an operation to the #out JSON array for processing later.
296 : *
297 : * @param op_name name of the operation
298 : * @param op_value values for the operation (consumed)
299 : */
300 : static void
301 180 : output_operation (const char *op_name,
302 : json_t *op_value)
303 : {
304 : json_t *action;
305 :
306 180 : GNUNET_assert (NULL != out);
307 180 : action = GNUNET_JSON_PACK (
308 : GNUNET_JSON_pack_string ("operation",
309 : op_name),
310 : GNUNET_JSON_pack_object_steal ("arguments",
311 : op_value));
312 180 : GNUNET_break (0 ==
313 : json_array_append_new (out,
314 : action));
315 180 : }
316 :
317 :
318 : /**
319 : * Information about a subroutine for an upload.
320 : */
321 : struct UploadHandler
322 : {
323 : /**
324 : * Key to trigger this subroutine.
325 : */
326 : const char *key;
327 :
328 : /**
329 : * Function implementing an upload.
330 : *
331 : * @param exchange_url URL of the exchange
332 : * @param idx index of the operation we are performing
333 : * @param value arguments to drive the upload.
334 : */
335 : void (*cb)(const char *exchange_url,
336 : size_t idx,
337 : const json_t *value);
338 :
339 : };
340 :
341 :
342 : /**
343 : * Load the offline key (if not yet done). Triggers shutdown on failure.
344 : *
345 : * @param do_create #GNUNET_YES if the key may be created
346 : * @return #GNUNET_OK on success
347 : */
348 : static int
349 6 : load_offline_key (int do_create)
350 : {
351 : static bool done;
352 : int ret;
353 : char *fn;
354 :
355 6 : if (done)
356 0 : return GNUNET_OK;
357 6 : if (GNUNET_OK !=
358 6 : GNUNET_CONFIGURATION_get_value_filename (kcfg,
359 : "auditor",
360 : "AUDITOR_PRIV_FILE",
361 : &fn))
362 : {
363 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
364 : "auditor",
365 : "AUDITOR_PRIV_FILE");
366 0 : global_ret = EXIT_NOTCONFIGURED;
367 0 : test_shutdown ();
368 0 : return GNUNET_SYSERR;
369 : }
370 6 : ret = GNUNET_CRYPTO_eddsa_key_from_file (fn,
371 : do_create,
372 : &auditor_priv.eddsa_priv);
373 6 : if (GNUNET_SYSERR == ret)
374 : {
375 0 : if (do_create)
376 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
377 : "Failed to initialize auditor key at `%s': %s\n",
378 : fn,
379 : "could not create file");
380 : else
381 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
382 : "Failed to load auditor key from file `%s': try running `taler-auditor-offline setup'?\n",
383 : fn);
384 0 : GNUNET_free (fn);
385 0 : global_ret = EXIT_FAILURE;
386 0 : test_shutdown ();
387 0 : return GNUNET_SYSERR;
388 : }
389 6 : GNUNET_free (fn);
390 6 : GNUNET_CRYPTO_eddsa_key_get_public (&auditor_priv.eddsa_priv,
391 : &auditor_pub.eddsa_pub);
392 6 : done = true;
393 6 : return GNUNET_OK;
394 : }
395 :
396 :
397 : /**
398 : * Function called with information about the post denomination (signature)
399 : * add operation result.
400 : *
401 : * @param dar the add request
402 : * @param adr response data
403 : */
404 : static void
405 180 : denomination_add_cb (
406 : struct DenominationAddRequest *dar,
407 : const struct TALER_EXCHANGE_PostAuditorsResponse *adr)
408 : {
409 180 : const struct TALER_EXCHANGE_HttpResponse *hr = &adr->hr;
410 :
411 180 : if (MHD_HTTP_NO_CONTENT != hr->http_status)
412 : {
413 0 : fprintf (stderr,
414 : "Upload failed for command #%u with status %u: %s (%s)\n",
415 0 : (unsigned int) dar->idx,
416 0 : hr->http_status,
417 0 : TALER_ErrorCode_get_hint (hr->ec),
418 0 : NULL != hr->hint
419 : ? hr->hint
420 : : "no hint provided");
421 0 : global_ret = EXIT_FAILURE;
422 : }
423 180 : GNUNET_CONTAINER_DLL_remove (dar_head,
424 : dar_tail,
425 : dar);
426 180 : GNUNET_free (dar);
427 180 : test_shutdown ();
428 180 : }
429 :
430 :
431 : /**
432 : * Upload denomination add data.
433 : *
434 : * @param exchange_url base URL of the exchange
435 : * @param idx index of the operation we are performing (for logging)
436 : * @param value arguments for adding denominations
437 : */
438 : static void
439 180 : upload_denomination_add (const char *exchange_url,
440 : size_t idx,
441 : const json_t *value)
442 : {
443 : struct TALER_AuditorSignatureP auditor_sig;
444 : struct TALER_DenominationHashP h_denom_pub;
445 : struct DenominationAddRequest *dar;
446 : const char *err_name;
447 : unsigned int err_line;
448 : struct GNUNET_JSON_Specification spec[] = {
449 180 : GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
450 : &h_denom_pub),
451 180 : GNUNET_JSON_spec_fixed_auto ("auditor_sig",
452 : &auditor_sig),
453 180 : GNUNET_JSON_spec_end ()
454 : };
455 :
456 180 : if (GNUNET_OK !=
457 180 : GNUNET_JSON_parse (value,
458 : spec,
459 : &err_name,
460 : &err_line))
461 : {
462 0 : fprintf (stderr,
463 : "Invalid input for adding denomination: %s#%u at %u (skipping)\n",
464 : err_name,
465 : err_line,
466 : (unsigned int) idx);
467 0 : global_ret = EXIT_FAILURE;
468 0 : test_shutdown ();
469 0 : return;
470 : }
471 180 : dar = GNUNET_new (struct DenominationAddRequest);
472 180 : dar->idx = idx;
473 180 : dar->h = TALER_EXCHANGE_post_auditors_create (ctx,
474 : exchange_url,
475 : &h_denom_pub,
476 : &auditor_pub,
477 : &auditor_sig);
478 180 : GNUNET_assert (NULL != dar->h);
479 180 : GNUNET_assert (TALER_EC_NONE ==
480 : TALER_EXCHANGE_post_auditors_start (dar->h,
481 : &denomination_add_cb,
482 : dar));
483 180 : GNUNET_CONTAINER_DLL_insert (dar_head,
484 : dar_tail,
485 : dar);
486 : }
487 :
488 :
489 : /**
490 : * Perform uploads based on the JSON in #out.
491 : *
492 : * @param exchange_url base URL of the exchange to use
493 : */
494 : static void
495 6 : trigger_upload (const char *exchange_url)
496 : {
497 6 : struct UploadHandler uhs[] = {
498 : {
499 : .key = OP_SIGN_DENOMINATION,
500 : .cb = &upload_denomination_add
501 : },
502 : /* array termination */
503 : {
504 : .key = NULL
505 : }
506 : };
507 : size_t index;
508 : json_t *obj;
509 :
510 186 : json_array_foreach (out, index, obj) {
511 180 : bool found = false;
512 : const char *key;
513 : const json_t *value;
514 :
515 180 : key = json_string_value (json_object_get (obj, "operation"));
516 180 : value = json_object_get (obj, "arguments");
517 180 : if (NULL == key)
518 : {
519 0 : fprintf (stderr,
520 : "Malformed JSON input\n");
521 0 : global_ret = EXIT_FAILURE;
522 0 : test_shutdown ();
523 0 : return;
524 : }
525 : /* block of code that uses key and value */
526 180 : for (unsigned int i = 0; NULL != uhs[i].key; i++)
527 : {
528 180 : if (0 == strcasecmp (key,
529 : uhs[i].key))
530 : {
531 180 : found = true;
532 180 : uhs[i].cb (exchange_url,
533 : index,
534 : value);
535 180 : break;
536 : }
537 : }
538 180 : if (! found)
539 : {
540 0 : fprintf (stderr,
541 : "Upload does not know how to handle `%s'\n",
542 : key);
543 0 : global_ret = EXIT_FAILURE;
544 0 : test_shutdown ();
545 0 : return;
546 : }
547 : }
548 : /* test here, in case no upload was triggered (i.e. empty input) */
549 6 : test_shutdown ();
550 : }
551 :
552 :
553 : /**
554 : * Upload operation result (signatures) to exchange.
555 : *
556 : * @param args the array of command-line arguments to process next
557 : */
558 : static void
559 6 : do_upload (char *const *args)
560 : {
561 : char *exchange_url;
562 :
563 : (void) args;
564 6 : if (GNUNET_YES == GNUNET_is_zero (&auditor_pub))
565 : {
566 : /* private key not available, try configuration for public key */
567 : char *auditor_public_key_str;
568 :
569 0 : if (GNUNET_OK !=
570 0 : GNUNET_CONFIGURATION_get_value_string (kcfg,
571 : "auditor",
572 : "PUBLIC_KEY",
573 : &auditor_public_key_str))
574 : {
575 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
576 : "auditor",
577 : "PUBLIC_KEY");
578 0 : global_ret = EXIT_NOTCONFIGURED;
579 0 : test_shutdown ();
580 0 : return;
581 : }
582 0 : if (GNUNET_OK !=
583 0 : GNUNET_CRYPTO_eddsa_public_key_from_string (
584 : auditor_public_key_str,
585 : strlen (auditor_public_key_str),
586 : &auditor_pub.eddsa_pub))
587 : {
588 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
589 : "auditor",
590 : "PUBLIC_KEY",
591 : "invalid key");
592 0 : GNUNET_free (auditor_public_key_str);
593 0 : global_ret = EXIT_NOTCONFIGURED;
594 0 : test_shutdown ();
595 0 : return;
596 : }
597 0 : GNUNET_free (auditor_public_key_str);
598 : }
599 6 : if (NULL != in)
600 : {
601 0 : fprintf (stderr,
602 : "Downloaded data was not consumed, refusing upload\n");
603 0 : test_shutdown ();
604 0 : global_ret = EXIT_FAILURE;
605 0 : return;
606 : }
607 6 : if (NULL == out)
608 : {
609 : json_error_t err;
610 :
611 0 : out = json_loadf (stdin,
612 : JSON_REJECT_DUPLICATES,
613 : &err);
614 0 : if (NULL == out)
615 : {
616 0 : fprintf (stderr,
617 : "Failed to read JSON input: %s at %d:%s (offset: %d)\n",
618 : err.text,
619 : err.line,
620 : err.source,
621 : err.position);
622 0 : test_shutdown ();
623 0 : global_ret = EXIT_FAILURE;
624 0 : return;
625 : }
626 : }
627 6 : if (! json_is_array (out))
628 : {
629 0 : fprintf (stderr,
630 : "Error: expected JSON array for `upload` command\n");
631 0 : test_shutdown ();
632 0 : global_ret = EXIT_FAILURE;
633 0 : return;
634 : }
635 6 : if (GNUNET_OK !=
636 6 : GNUNET_CONFIGURATION_get_value_string (kcfg,
637 : "exchange",
638 : "BASE_URL",
639 : &exchange_url))
640 : {
641 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
642 : "exchange",
643 : "BASE_URL");
644 0 : global_ret = EXIT_NOTCONFIGURED;
645 0 : test_shutdown ();
646 0 : return;
647 : }
648 6 : trigger_upload (exchange_url);
649 6 : json_decref (out);
650 6 : out = NULL;
651 6 : GNUNET_free (exchange_url);
652 : }
653 :
654 :
655 : /**
656 : * Function called with information about who is auditing
657 : * a particular exchange and what keys the exchange is using.
658 : *
659 : * @param args the remaining args
660 : * @param kr response data
661 : * @param keys key data from the exchange
662 : */
663 : static void
664 6 : keys_cb (
665 : char *const *args,
666 : const struct TALER_EXCHANGE_KeysResponse *kr,
667 : struct TALER_EXCHANGE_Keys *keys)
668 : {
669 6 : exchange = NULL;
670 6 : switch (kr->hr.http_status)
671 : {
672 6 : case MHD_HTTP_OK:
673 6 : if (NULL == kr->hr.reply)
674 : {
675 0 : GNUNET_break_op (0);
676 0 : test_shutdown ();
677 0 : global_ret = EXIT_FAILURE;
678 0 : if (NULL != keys)
679 0 : TALER_EXCHANGE_keys_decref (keys);
680 0 : return;
681 : }
682 6 : break;
683 0 : default:
684 0 : fprintf (stderr,
685 : "Failed to download keys: %s (HTTP status: %u/EC: %u)\n",
686 0 : kr->hr.hint,
687 0 : kr->hr.http_status,
688 0 : (unsigned int) kr->hr.ec);
689 0 : test_shutdown ();
690 0 : global_ret = EXIT_FAILURE;
691 0 : return;
692 : }
693 6 : in = GNUNET_JSON_PACK (
694 : GNUNET_JSON_pack_string ("operation",
695 : OP_INPUT_KEYS),
696 : GNUNET_JSON_pack_object_incref ("arguments",
697 : (json_t *) kr->hr.reply));
698 6 : if (NULL == args[0])
699 : {
700 0 : json_dumpf (in,
701 : stdout,
702 : JSON_INDENT (2));
703 0 : json_decref (in);
704 0 : in = NULL;
705 : }
706 6 : next (args);
707 6 : TALER_EXCHANGE_keys_decref (keys);
708 : }
709 :
710 :
711 : /**
712 : * Download future keys.
713 : *
714 : * @param args the array of command-line arguments to process next
715 : */
716 : static void
717 6 : do_download (char *const *args)
718 : {
719 : char *exchange_url;
720 :
721 6 : if (GNUNET_OK !=
722 6 : GNUNET_CONFIGURATION_get_value_string (kcfg,
723 : "exchange",
724 : "BASE_URL",
725 : &exchange_url))
726 : {
727 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
728 : "exchange",
729 : "BASE_URL");
730 0 : test_shutdown ();
731 0 : global_ret = EXIT_NOTCONFIGURED;
732 0 : return;
733 : }
734 6 : exchange = TALER_EXCHANGE_get_keys_create (ctx,
735 : exchange_url);
736 6 : GNUNET_assert (TALER_EC_NONE ==
737 : TALER_EXCHANGE_get_keys_start (exchange,
738 : &keys_cb,
739 : args));
740 6 : GNUNET_free (exchange_url);
741 : }
742 :
743 :
744 : /**
745 : * Output @a denomkeys for human consumption.
746 : *
747 : * @param denomkeys keys to output
748 : * @return #GNUNET_OK on success
749 : */
750 : static enum GNUNET_GenericReturnValue
751 0 : show_denomkeys (const json_t *denomkeys)
752 : {
753 : size_t index;
754 : json_t *value;
755 :
756 0 : json_array_foreach (denomkeys, index, value) {
757 : struct TALER_DenominationGroup group;
758 : const json_t *denoms;
759 : const char *err_name;
760 : unsigned int err_line;
761 : struct GNUNET_JSON_Specification spec[] = {
762 0 : TALER_JSON_spec_denomination_group (NULL,
763 : currency,
764 : &group),
765 0 : GNUNET_JSON_spec_array_const ("denoms",
766 : &denoms),
767 0 : GNUNET_JSON_spec_end ()
768 : };
769 : size_t index2;
770 : json_t *value2;
771 :
772 0 : if (GNUNET_OK !=
773 0 : GNUNET_JSON_parse (value,
774 : spec,
775 : &err_name,
776 : &err_line))
777 : {
778 0 : fprintf (stderr,
779 : "Invalid input for denomination key to 'show': %s#%u at %u (skipping)\n",
780 : err_name,
781 : err_line,
782 : (unsigned int) index);
783 0 : GNUNET_JSON_parse_free (spec);
784 0 : global_ret = EXIT_FAILURE;
785 0 : test_shutdown ();
786 0 : return GNUNET_SYSERR;
787 : }
788 0 : json_array_foreach (denoms, index2, value2) {
789 : struct GNUNET_TIME_Timestamp stamp_start;
790 : struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
791 : struct GNUNET_TIME_Timestamp stamp_expire_deposit;
792 : struct GNUNET_TIME_Timestamp stamp_expire_legal;
793 : struct TALER_DenominationPublicKey denom_pub;
794 : struct TALER_MasterSignatureP master_sig;
795 : struct GNUNET_JSON_Specification ispec[] = {
796 0 : TALER_JSON_spec_denom_pub_cipher (NULL,
797 : group.cipher,
798 : &denom_pub),
799 0 : GNUNET_JSON_spec_timestamp ("stamp_start",
800 : &stamp_start),
801 0 : GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
802 : &stamp_expire_withdraw),
803 0 : GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
804 : &stamp_expire_deposit),
805 0 : GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
806 : &stamp_expire_legal),
807 0 : GNUNET_JSON_spec_fixed_auto ("master_sig",
808 : &master_sig),
809 0 : GNUNET_JSON_spec_end ()
810 : };
811 : struct GNUNET_TIME_Relative duration;
812 : struct TALER_DenominationHashP h_denom_pub;
813 :
814 0 : if (GNUNET_OK !=
815 0 : GNUNET_JSON_parse (value2,
816 : ispec,
817 : &err_name,
818 : &err_line))
819 : {
820 0 : fprintf (stderr,
821 : "Invalid input for denomination key to 'show': %s#%u at %u/%u (skipping)\n",
822 : err_name,
823 : err_line,
824 : (unsigned int) index,
825 : (unsigned int) index2);
826 0 : GNUNET_JSON_parse_free (spec);
827 0 : global_ret = EXIT_FAILURE;
828 0 : test_shutdown ();
829 0 : return GNUNET_SYSERR;
830 : }
831 0 : duration = GNUNET_TIME_absolute_get_difference (
832 : stamp_start.abs_time,
833 : stamp_expire_withdraw.abs_time);
834 0 : TALER_denom_pub_hash (&denom_pub,
835 : &h_denom_pub);
836 0 : if (GNUNET_OK !=
837 0 : TALER_exchange_offline_denom_validity_verify (
838 : &h_denom_pub,
839 : stamp_start,
840 : stamp_expire_withdraw,
841 : stamp_expire_deposit,
842 : stamp_expire_legal,
843 : &group.value,
844 : &group.fees,
845 : &master_pub,
846 : &master_sig))
847 : {
848 0 : fprintf (stderr,
849 : "Invalid master signature for key %s (aborting)\n",
850 : TALER_B2S (&h_denom_pub));
851 0 : global_ret = EXIT_FAILURE;
852 0 : GNUNET_JSON_parse_free (ispec);
853 0 : GNUNET_JSON_parse_free (spec);
854 0 : test_shutdown ();
855 0 : return GNUNET_SYSERR;
856 : }
857 :
858 : {
859 : char *withdraw_fee_s;
860 : char *deposit_fee_s;
861 : char *refresh_fee_s;
862 : char *refund_fee_s;
863 : char *deposit_s;
864 : char *legal_s;
865 :
866 0 : withdraw_fee_s = TALER_amount_to_string (&group.fees.withdraw);
867 0 : deposit_fee_s = TALER_amount_to_string (&group.fees.deposit);
868 0 : refresh_fee_s = TALER_amount_to_string (&group.fees.refresh);
869 0 : refund_fee_s = TALER_amount_to_string (&group.fees.refund);
870 0 : deposit_s = GNUNET_strdup (
871 : GNUNET_TIME_timestamp2s (stamp_expire_deposit));
872 0 : legal_s = GNUNET_strdup (
873 : GNUNET_TIME_timestamp2s (stamp_expire_legal));
874 :
875 0 : printf (
876 : "DENOMINATION-KEY %s of value %s starting at %s "
877 : "(used for: %s, deposit until: %s legal end: %s) with fees %s/%s/%s/%s\n",
878 : TALER_B2S (&h_denom_pub),
879 : TALER_amount2s (&group.value),
880 : GNUNET_TIME_timestamp2s (stamp_start),
881 : GNUNET_TIME_relative2s (duration,
882 : false),
883 : deposit_s,
884 : legal_s,
885 : withdraw_fee_s,
886 : deposit_fee_s,
887 : refresh_fee_s,
888 : refund_fee_s);
889 0 : GNUNET_free (withdraw_fee_s);
890 0 : GNUNET_free (deposit_fee_s);
891 0 : GNUNET_free (refresh_fee_s);
892 0 : GNUNET_free (refund_fee_s);
893 0 : GNUNET_free (deposit_s);
894 0 : GNUNET_free (legal_s);
895 : }
896 0 : GNUNET_JSON_parse_free (ispec);
897 : }
898 0 : GNUNET_JSON_parse_free (spec);
899 : }
900 0 : return GNUNET_OK;
901 : }
902 :
903 :
904 : /**
905 : * Parse the '/keys' input for operation called @a command_name.
906 : *
907 : * @param command_name name of the command, for logging errors
908 : * @return NULL if the input is malformed
909 : */
910 : static json_t *
911 6 : parse_keys (const char *command_name)
912 : {
913 : json_t *keys;
914 : const char *op_str;
915 : struct GNUNET_JSON_Specification spec[] = {
916 6 : GNUNET_JSON_spec_json ("arguments",
917 : &keys),
918 6 : GNUNET_JSON_spec_string ("operation",
919 : &op_str),
920 6 : GNUNET_JSON_spec_end ()
921 : };
922 : const char *err_name;
923 : unsigned int err_line;
924 :
925 6 : if (NULL == in)
926 : {
927 : json_error_t err;
928 :
929 0 : in = json_loadf (stdin,
930 : JSON_REJECT_DUPLICATES,
931 : &err);
932 0 : if (NULL == in)
933 : {
934 0 : fprintf (stderr,
935 : "Failed to read JSON input: %s at %d:%s (offset: %d)\n",
936 : err.text,
937 : err.line,
938 : err.source,
939 : err.position);
940 0 : global_ret = EXIT_FAILURE;
941 0 : test_shutdown ();
942 0 : return NULL;
943 : }
944 : }
945 6 : if (GNUNET_OK !=
946 6 : GNUNET_JSON_parse (in,
947 : spec,
948 : &err_name,
949 : &err_line))
950 : {
951 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
952 : "Invalid input to '%s': %s#%u (skipping)\n",
953 : command_name,
954 : err_name,
955 : err_line);
956 0 : json_dumpf (in,
957 : stderr,
958 : JSON_INDENT (2));
959 0 : global_ret = EXIT_FAILURE;
960 0 : test_shutdown ();
961 0 : return NULL;
962 : }
963 6 : if (0 != strcmp (op_str,
964 : OP_INPUT_KEYS))
965 : {
966 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
967 : "Invalid input to '%s' : operation is `%s', expected `%s'\n",
968 : command_name,
969 : op_str,
970 : OP_INPUT_KEYS);
971 0 : GNUNET_JSON_parse_free (spec);
972 0 : global_ret = EXIT_FAILURE;
973 0 : test_shutdown ();
974 0 : return NULL;
975 : }
976 6 : json_decref (in);
977 6 : in = NULL;
978 6 : return keys;
979 : }
980 :
981 :
982 : /**
983 : * Show exchange denomination keys.
984 : *
985 : * @param args the array of command-line arguments to process next
986 : */
987 : static void
988 0 : do_show (char *const *args)
989 : {
990 : json_t *keys;
991 : const char *err_name;
992 : unsigned int err_line;
993 : const json_t *denomkeys;
994 : struct TALER_MasterPublicKeyP mpub;
995 : struct GNUNET_JSON_Specification spec[] = {
996 0 : GNUNET_JSON_spec_array_const ("denominations",
997 : &denomkeys),
998 0 : GNUNET_JSON_spec_fixed_auto ("master_public_key",
999 : &mpub),
1000 0 : GNUNET_JSON_spec_end ()
1001 : };
1002 :
1003 0 : keys = parse_keys ("show");
1004 0 : if (NULL == keys)
1005 : {
1006 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1007 : "Showing failed: no valid input\n");
1008 0 : return;
1009 : }
1010 0 : if (GNUNET_OK !=
1011 0 : GNUNET_JSON_parse (keys,
1012 : spec,
1013 : &err_name,
1014 : &err_line))
1015 : {
1016 0 : fprintf (stderr,
1017 : "Invalid input to 'show': %s#%u (skipping)\n",
1018 : err_name,
1019 : err_line);
1020 0 : global_ret = EXIT_FAILURE;
1021 0 : test_shutdown ();
1022 0 : json_decref (keys);
1023 0 : return;
1024 : }
1025 0 : if (0 !=
1026 0 : GNUNET_memcmp (&mpub,
1027 : &master_pub))
1028 : {
1029 0 : fprintf (stderr,
1030 : "Exchange master public key does not match key we have configured (aborting)\n");
1031 0 : global_ret = EXIT_FAILURE;
1032 0 : test_shutdown ();
1033 0 : json_decref (keys);
1034 0 : return;
1035 : }
1036 0 : if (GNUNET_OK !=
1037 0 : show_denomkeys (denomkeys))
1038 : {
1039 0 : global_ret = EXIT_FAILURE;
1040 0 : test_shutdown ();
1041 0 : json_decref (keys);
1042 0 : return;
1043 : }
1044 0 : json_decref (keys);
1045 : /* do NOT consume input if next argument is '-' */
1046 0 : if ( (NULL != args[0]) &&
1047 0 : (0 == strcmp ("-",
1048 : args[0])) )
1049 : {
1050 0 : next (args + 1);
1051 0 : return;
1052 : }
1053 0 : next (args);
1054 : }
1055 :
1056 :
1057 : /**
1058 : * Sign @a denomkeys with offline key.
1059 : *
1060 : * @param denomkeys keys to output
1061 : * @return #GNUNET_OK on success
1062 : */
1063 : static enum GNUNET_GenericReturnValue
1064 6 : sign_denomkeys (const json_t *denomkeys)
1065 : {
1066 : size_t group_idx;
1067 : json_t *value;
1068 :
1069 66 : json_array_foreach (denomkeys, group_idx, value) {
1070 60 : struct TALER_DenominationGroup group = { 0 };
1071 : const json_t *denom_keys_array;
1072 : const char *err_name;
1073 : unsigned int err_line;
1074 : struct GNUNET_JSON_Specification spec[] = {
1075 60 : TALER_JSON_spec_denomination_group (NULL,
1076 : currency,
1077 : &group),
1078 60 : GNUNET_JSON_spec_array_const ("denoms",
1079 : &denom_keys_array),
1080 60 : GNUNET_JSON_spec_end ()
1081 : };
1082 : size_t index;
1083 : json_t *denom_key_obj;
1084 :
1085 60 : if (GNUNET_OK !=
1086 60 : GNUNET_JSON_parse (value,
1087 : spec,
1088 : &err_name,
1089 : &err_line))
1090 : {
1091 0 : fprintf (stderr,
1092 : "Invalid input for denomination key to 'sign': %s#%u at %u (skipping)\n",
1093 : err_name,
1094 : err_line,
1095 : (unsigned int) group_idx);
1096 0 : GNUNET_JSON_parse_free (spec);
1097 0 : global_ret = EXIT_FAILURE;
1098 0 : test_shutdown ();
1099 0 : return GNUNET_SYSERR;
1100 : }
1101 240 : json_array_foreach (denom_keys_array, index, denom_key_obj) {
1102 : struct GNUNET_TIME_Timestamp stamp_start;
1103 : struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
1104 : struct GNUNET_TIME_Timestamp stamp_expire_deposit;
1105 : struct GNUNET_TIME_Timestamp stamp_expire_legal;
1106 180 : struct TALER_DenominationPublicKey denom_pub = {
1107 : .age_mask = group.age_mask
1108 : };
1109 : struct TALER_MasterSignatureP master_sig;
1110 : struct GNUNET_JSON_Specification ispec[] = {
1111 180 : TALER_JSON_spec_denom_pub_cipher (NULL,
1112 : group.cipher,
1113 : &denom_pub),
1114 180 : GNUNET_JSON_spec_timestamp ("stamp_start",
1115 : &stamp_start),
1116 180 : GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
1117 : &stamp_expire_withdraw),
1118 180 : GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
1119 : &stamp_expire_deposit),
1120 180 : GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
1121 : &stamp_expire_legal),
1122 180 : GNUNET_JSON_spec_fixed_auto ("master_sig",
1123 : &master_sig),
1124 180 : GNUNET_JSON_spec_end ()
1125 : };
1126 : struct TALER_DenominationHashP h_denom_pub;
1127 :
1128 180 : if (GNUNET_OK !=
1129 180 : GNUNET_JSON_parse (denom_key_obj,
1130 : ispec,
1131 : &err_name,
1132 : &err_line))
1133 : {
1134 0 : fprintf (stderr,
1135 : "Invalid input for denomination key to 'sign': %s#%u at %u/%u (skipping)\n",
1136 : err_name,
1137 : err_line,
1138 : (unsigned int) group_idx,
1139 : (unsigned int) index);
1140 0 : GNUNET_JSON_parse_free (spec);
1141 0 : global_ret = EXIT_FAILURE;
1142 0 : test_shutdown ();
1143 0 : return GNUNET_SYSERR;
1144 : }
1145 180 : TALER_denom_pub_hash (&denom_pub,
1146 : &h_denom_pub);
1147 180 : if (GNUNET_OK !=
1148 180 : TALER_exchange_offline_denom_validity_verify (
1149 : &h_denom_pub,
1150 : stamp_start,
1151 : stamp_expire_withdraw,
1152 : stamp_expire_deposit,
1153 : stamp_expire_legal,
1154 : &group.value,
1155 : &group.fees,
1156 : &master_pub,
1157 : &master_sig))
1158 : {
1159 0 : fprintf (stderr,
1160 : "Invalid master signature for key %s (aborting)\n",
1161 : TALER_B2S (&h_denom_pub));
1162 0 : global_ret = EXIT_FAILURE;
1163 0 : test_shutdown ();
1164 0 : return GNUNET_SYSERR;
1165 : }
1166 :
1167 : {
1168 : struct TALER_AuditorSignatureP auditor_sig;
1169 :
1170 180 : TALER_auditor_denom_validity_sign (auditor_url,
1171 : &h_denom_pub,
1172 : &master_pub,
1173 : stamp_start,
1174 : stamp_expire_withdraw,
1175 : stamp_expire_deposit,
1176 : stamp_expire_legal,
1177 : &group.value,
1178 : &group.fees,
1179 : &auditor_priv,
1180 : &auditor_sig);
1181 180 : output_operation (OP_SIGN_DENOMINATION,
1182 180 : GNUNET_JSON_PACK (
1183 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
1184 : &h_denom_pub),
1185 : GNUNET_JSON_pack_data_auto ("auditor_sig",
1186 : &auditor_sig)));
1187 : }
1188 180 : GNUNET_JSON_parse_free (ispec);
1189 : }
1190 60 : GNUNET_JSON_parse_free (spec);
1191 : }
1192 6 : return GNUNET_OK;
1193 : }
1194 :
1195 :
1196 : /**
1197 : * Sign denomination keys.
1198 : *
1199 : * @param args the array of command-line arguments to process next
1200 : */
1201 : static void
1202 6 : do_sign (char *const *args)
1203 : {
1204 : json_t *keys;
1205 : const char *err_name;
1206 : unsigned int err_line;
1207 : struct TALER_MasterPublicKeyP mpub;
1208 : const json_t *denomkeys;
1209 : struct GNUNET_JSON_Specification spec[] = {
1210 6 : GNUNET_JSON_spec_array_const ("denominations",
1211 : &denomkeys),
1212 6 : GNUNET_JSON_spec_fixed_auto ("master_public_key",
1213 : &mpub),
1214 6 : GNUNET_JSON_spec_end ()
1215 : };
1216 :
1217 6 : keys = parse_keys ("sign");
1218 6 : if (NULL == keys)
1219 : {
1220 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1221 : "Signing failed: no valid input\n");
1222 0 : return;
1223 : }
1224 6 : if (GNUNET_OK !=
1225 6 : load_offline_key (GNUNET_NO))
1226 : {
1227 0 : json_decref (keys);
1228 0 : return;
1229 : }
1230 6 : if (GNUNET_OK !=
1231 6 : GNUNET_JSON_parse (keys,
1232 : spec,
1233 : &err_name,
1234 : &err_line))
1235 : {
1236 0 : fprintf (stderr,
1237 : "Invalid input to 'sign': %s#%u (skipping)\n",
1238 : err_name,
1239 : err_line);
1240 0 : global_ret = EXIT_FAILURE;
1241 0 : test_shutdown ();
1242 0 : json_decref (keys);
1243 0 : return;
1244 : }
1245 6 : if (0 !=
1246 6 : GNUNET_memcmp (&mpub,
1247 : &master_pub))
1248 : {
1249 0 : fprintf (stderr,
1250 : "Exchange master public key does not match key we have configured (aborting)\n");
1251 0 : global_ret = EXIT_FAILURE;
1252 0 : test_shutdown ();
1253 0 : json_decref (keys);
1254 0 : return;
1255 : }
1256 6 : if (NULL == out)
1257 : {
1258 6 : out = json_array ();
1259 6 : GNUNET_assert (NULL != out);
1260 : }
1261 6 : if (GNUNET_OK !=
1262 6 : sign_denomkeys (denomkeys))
1263 : {
1264 0 : global_ret = EXIT_FAILURE;
1265 0 : test_shutdown ();
1266 0 : json_decref (keys);
1267 0 : return;
1268 : }
1269 6 : json_decref (keys);
1270 6 : next (args);
1271 : }
1272 :
1273 :
1274 : /**
1275 : * Setup and output offline signing key.
1276 : *
1277 : * @param args the array of command-line arguments to process next
1278 : */
1279 : static void
1280 0 : do_setup (char *const *args)
1281 : {
1282 0 : if (GNUNET_OK !=
1283 0 : load_offline_key (GNUNET_YES))
1284 : {
1285 0 : global_ret = EXIT_FAILURE;
1286 0 : return;
1287 : }
1288 0 : if (NULL != *args)
1289 : {
1290 0 : if (NULL == out)
1291 : {
1292 0 : out = json_array ();
1293 0 : GNUNET_assert (NULL != out);
1294 : }
1295 0 : output_operation (OP_SETUP,
1296 0 : GNUNET_JSON_PACK (
1297 : GNUNET_JSON_pack_data_auto ("auditor_pub",
1298 : &auditor_pub)));
1299 : }
1300 :
1301 : else
1302 : {
1303 : char *pub_s;
1304 :
1305 0 : pub_s = GNUNET_STRINGS_data_to_string_alloc (&auditor_pub,
1306 : sizeof (auditor_pub));
1307 0 : fprintf (stdout,
1308 : "%s\n",
1309 : pub_s);
1310 0 : GNUNET_free (pub_s);
1311 : }
1312 0 : if ( (NULL != *args) &&
1313 0 : (0 == strcmp (*args,
1314 : "-")) )
1315 0 : args++;
1316 0 : next (args);
1317 : }
1318 :
1319 :
1320 : static void
1321 18 : work (void *cls)
1322 : {
1323 18 : char *const *args = cls;
1324 18 : struct SubCommand cmds[] = {
1325 : {
1326 : .name = "setup",
1327 : .help =
1328 : "setup auditor offline private key and show the public key",
1329 : .cb = &do_setup
1330 : },
1331 : {
1332 : .name = "download",
1333 : .help =
1334 : "obtain keys from exchange (to be performed online!)",
1335 : .cb = &do_download
1336 : },
1337 : {
1338 : .name = "show",
1339 : .help =
1340 : "display keys from exchange for human review (pass '-' as argument to disable consuming input)",
1341 : .cb = &do_show
1342 : },
1343 : {
1344 : .name = "sign",
1345 : .help =
1346 : "sign all denomination keys from the input",
1347 : .cb = &do_sign
1348 : },
1349 : {
1350 : .name = "upload",
1351 : .help =
1352 : "upload operation result to exchange (to be performed online!)",
1353 : .cb = &do_upload
1354 : },
1355 : /* list terminator */
1356 : {
1357 : .name = NULL,
1358 : }
1359 : };
1360 : (void) cls;
1361 :
1362 18 : nxt = NULL;
1363 66 : for (unsigned int i = 0; NULL != cmds[i].name; i++)
1364 : {
1365 66 : if (0 == strcasecmp (cmds[i].name,
1366 : args[0]))
1367 : {
1368 18 : cmds[i].cb (&args[1]);
1369 18 : return;
1370 : }
1371 : }
1372 :
1373 0 : if (0 != strcasecmp ("help",
1374 : args[0]))
1375 : {
1376 0 : fprintf (stderr,
1377 : "Unexpected command `%s'\n",
1378 : args[0]);
1379 0 : global_ret = EXIT_INVALIDARGUMENT;
1380 : }
1381 0 : fprintf (stderr,
1382 : "Supported subcommands:\n");
1383 0 : for (unsigned int i = 0; NULL != cmds[i].name; i++)
1384 : {
1385 0 : fprintf (stderr,
1386 : "\t%s - %s\n",
1387 : cmds[i].name,
1388 : cmds[i].help);
1389 : }
1390 : }
1391 :
1392 :
1393 : /**
1394 : * Main function that will be run.
1395 : *
1396 : * @param cls closure
1397 : * @param args remaining command-line arguments
1398 : * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1399 : * @param cfg configuration
1400 : */
1401 : static void
1402 6 : run (void *cls,
1403 : char *const *args,
1404 : const char *cfgfile,
1405 : const struct GNUNET_CONFIGURATION_Handle *cfg)
1406 : {
1407 : (void) cls;
1408 : (void) cfgfile;
1409 6 : kcfg = cfg;
1410 6 : if (GNUNET_OK !=
1411 6 : TALER_config_get_currency (kcfg,
1412 : "exchange",
1413 : ¤cy))
1414 : {
1415 0 : global_ret = EXIT_NOTCONFIGURED;
1416 0 : return;
1417 : }
1418 6 : if (GNUNET_OK !=
1419 6 : GNUNET_CONFIGURATION_get_value_string (kcfg,
1420 : "auditor",
1421 : "BASE_URL",
1422 : &auditor_url))
1423 : {
1424 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1425 : "auditor",
1426 : "BASE_URL");
1427 0 : global_ret = EXIT_NOTCONFIGURED;
1428 0 : return;
1429 : }
1430 : {
1431 : char *master_public_key_str;
1432 :
1433 6 : if (GNUNET_OK !=
1434 6 : GNUNET_CONFIGURATION_get_value_string (cfg,
1435 : "exchange",
1436 : "MASTER_PUBLIC_KEY",
1437 : &master_public_key_str))
1438 : {
1439 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1440 : "exchange",
1441 : "MASTER_PUBLIC_KEY");
1442 0 : global_ret = EXIT_NOTCONFIGURED;
1443 0 : return;
1444 : }
1445 6 : if (GNUNET_OK !=
1446 6 : GNUNET_CRYPTO_eddsa_public_key_from_string (
1447 : master_public_key_str,
1448 : strlen (master_public_key_str),
1449 : &master_pub.eddsa_pub))
1450 : {
1451 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1452 : "Invalid master public key given in exchange configuration.");
1453 0 : GNUNET_free (master_public_key_str);
1454 0 : global_ret = EXIT_NOTCONFIGURED;
1455 0 : return;
1456 : }
1457 6 : GNUNET_free (master_public_key_str);
1458 : }
1459 6 : ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
1460 : &rc);
1461 6 : rc = GNUNET_CURL_gnunet_rc_create (ctx);
1462 6 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1463 : NULL);
1464 6 : next (args);
1465 : }
1466 :
1467 :
1468 : /**
1469 : * The main function of the taler-auditor-offline tool. This tool is used to
1470 : * sign denomination keys with the auditor's key. It uses the long-term
1471 : * offline private key of the auditor and generates signatures with it. It
1472 : * also supports online operations with the exchange to download its input
1473 : * data and to upload its results. Those online operations should be performed
1474 : * on another machine in production!
1475 : *
1476 : * @param argc number of arguments from the command line
1477 : * @param argv command line arguments
1478 : * @return 0 ok, 1 on error
1479 : */
1480 : int
1481 6 : main (int argc,
1482 : char *const *argv)
1483 : {
1484 6 : struct GNUNET_GETOPT_CommandLineOption options[] = {
1485 : GNUNET_GETOPT_OPTION_END
1486 : };
1487 : enum GNUNET_GenericReturnValue ret;
1488 :
1489 6 : ret = GNUNET_PROGRAM_run (
1490 : TALER_AUDITOR_project_data (),
1491 : argc, argv,
1492 : "taler-auditor-offline",
1493 : gettext_noop ("Operations for offline signing for a Taler exchange"),
1494 : options,
1495 : &run, NULL);
1496 6 : if (GNUNET_SYSERR == ret)
1497 0 : return EXIT_INVALIDARGUMENT;
1498 6 : if (GNUNET_NO == ret)
1499 0 : return EXIT_SUCCESS;
1500 6 : return global_ret;
1501 : }
1502 :
1503 :
1504 : /* end of taler-auditor-offline.c */
|