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 246 : test_shutdown (void)
257 : {
258 246 : if ( (NULL == dar_head) &&
259 6 : (NULL == exchange) &&
260 6 : (NULL == nxt) )
261 6 : GNUNET_SCHEDULER_shutdown ();
262 246 : }
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 240 : output_operation (const char *op_name,
302 : json_t *op_value)
303 : {
304 : json_t *action;
305 :
306 240 : GNUNET_assert (NULL != out);
307 240 : action = GNUNET_JSON_PACK (
308 : GNUNET_JSON_pack_string ("operation",
309 : op_name),
310 : GNUNET_JSON_pack_object_steal ("arguments",
311 : op_value));
312 240 : GNUNET_break (0 ==
313 : json_array_append_new (out,
314 : action));
315 240 : }
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 : test_shutdown ();
367 0 : return GNUNET_SYSERR;
368 : }
369 6 : ret = GNUNET_CRYPTO_eddsa_key_from_file (fn,
370 : do_create,
371 : &auditor_priv.eddsa_priv);
372 6 : if (GNUNET_SYSERR == ret)
373 : {
374 0 : if (do_create)
375 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
376 : "Failed to initialize auditor key at `%s': %s\n",
377 : fn,
378 : "could not create file");
379 : else
380 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
381 : "Failed to load auditor key from file `%s': try running `taler-auditor-offline setup'?\n",
382 : fn);
383 0 : GNUNET_free (fn);
384 0 : test_shutdown ();
385 0 : return GNUNET_SYSERR;
386 : }
387 6 : GNUNET_free (fn);
388 6 : GNUNET_CRYPTO_eddsa_key_get_public (&auditor_priv.eddsa_priv,
389 : &auditor_pub.eddsa_pub);
390 6 : done = true;
391 6 : return GNUNET_OK;
392 : }
393 :
394 :
395 : /**
396 : * Function called with information about the post denomination (signature)
397 : * add operation result.
398 : *
399 : * @param dar the add request
400 : * @param adr response data
401 : */
402 : static void
403 240 : denomination_add_cb (
404 : struct DenominationAddRequest *dar,
405 : const struct TALER_EXCHANGE_PostAuditorsResponse *adr)
406 : {
407 240 : const struct TALER_EXCHANGE_HttpResponse *hr = &adr->hr;
408 :
409 240 : if (MHD_HTTP_NO_CONTENT != hr->http_status)
410 : {
411 0 : fprintf (stderr,
412 : "Upload failed for command #%u with status %u: %s (%s)\n",
413 0 : (unsigned int) dar->idx,
414 0 : hr->http_status,
415 0 : TALER_ErrorCode_get_hint (hr->ec),
416 0 : NULL != hr->hint
417 : ? hr->hint
418 : : "no hint provided");
419 0 : global_ret = EXIT_FAILURE;
420 : }
421 240 : GNUNET_CONTAINER_DLL_remove (dar_head,
422 : dar_tail,
423 : dar);
424 240 : GNUNET_free (dar);
425 240 : test_shutdown ();
426 240 : }
427 :
428 :
429 : /**
430 : * Upload denomination add data.
431 : *
432 : * @param exchange_url base URL of the exchange
433 : * @param idx index of the operation we are performing (for logging)
434 : * @param value arguments for adding denominations
435 : */
436 : static void
437 240 : upload_denomination_add (const char *exchange_url,
438 : size_t idx,
439 : const json_t *value)
440 : {
441 : struct TALER_AuditorSignatureP auditor_sig;
442 : struct TALER_DenominationHashP h_denom_pub;
443 : struct DenominationAddRequest *dar;
444 : const char *err_name;
445 : unsigned int err_line;
446 : struct GNUNET_JSON_Specification spec[] = {
447 240 : GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
448 : &h_denom_pub),
449 240 : GNUNET_JSON_spec_fixed_auto ("auditor_sig",
450 : &auditor_sig),
451 240 : GNUNET_JSON_spec_end ()
452 : };
453 :
454 240 : if (GNUNET_OK !=
455 240 : GNUNET_JSON_parse (value,
456 : spec,
457 : &err_name,
458 : &err_line))
459 : {
460 0 : fprintf (stderr,
461 : "Invalid input for adding denomination: %s#%u at %u (skipping)\n",
462 : err_name,
463 : err_line,
464 : (unsigned int) idx);
465 0 : global_ret = EXIT_FAILURE;
466 0 : test_shutdown ();
467 0 : return;
468 : }
469 240 : dar = GNUNET_new (struct DenominationAddRequest);
470 240 : dar->idx = idx;
471 240 : dar->h = TALER_EXCHANGE_post_auditors_create (ctx,
472 : exchange_url,
473 : &h_denom_pub,
474 : &auditor_pub,
475 : &auditor_sig);
476 240 : GNUNET_assert (NULL != dar->h);
477 240 : GNUNET_assert (TALER_EC_NONE ==
478 : TALER_EXCHANGE_post_auditors_start (dar->h,
479 : &denomination_add_cb,
480 : dar));
481 240 : GNUNET_CONTAINER_DLL_insert (dar_head,
482 : dar_tail,
483 : dar);
484 : }
485 :
486 :
487 : /**
488 : * Perform uploads based on the JSON in #out.
489 : *
490 : * @param exchange_url base URL of the exchange to use
491 : */
492 : static void
493 6 : trigger_upload (const char *exchange_url)
494 : {
495 6 : struct UploadHandler uhs[] = {
496 : {
497 : .key = OP_SIGN_DENOMINATION,
498 : .cb = &upload_denomination_add
499 : },
500 : /* array termination */
501 : {
502 : .key = NULL
503 : }
504 : };
505 : size_t index;
506 : json_t *obj;
507 :
508 246 : json_array_foreach (out, index, obj) {
509 240 : bool found = false;
510 : const char *key;
511 : const json_t *value;
512 :
513 240 : key = json_string_value (json_object_get (obj, "operation"));
514 240 : value = json_object_get (obj, "arguments");
515 240 : if (NULL == key)
516 : {
517 0 : fprintf (stderr,
518 : "Malformed JSON input\n");
519 0 : global_ret = EXIT_FAILURE;
520 0 : test_shutdown ();
521 0 : return;
522 : }
523 : /* block of code that uses key and value */
524 240 : for (unsigned int i = 0; NULL != uhs[i].key; i++)
525 : {
526 240 : if (0 == strcasecmp (key,
527 : uhs[i].key))
528 : {
529 240 : found = true;
530 240 : uhs[i].cb (exchange_url,
531 : index,
532 : value);
533 240 : break;
534 : }
535 : }
536 240 : if (! found)
537 : {
538 0 : fprintf (stderr,
539 : "Upload does not know how to handle `%s'\n",
540 : key);
541 0 : global_ret = EXIT_FAILURE;
542 0 : test_shutdown ();
543 0 : return;
544 : }
545 : }
546 : /* test here, in case no upload was triggered (i.e. empty input) */
547 6 : test_shutdown ();
548 : }
549 :
550 :
551 : /**
552 : * Upload operation result (signatures) to exchange.
553 : *
554 : * @param args the array of command-line arguments to process next
555 : */
556 : static void
557 6 : do_upload (char *const *args)
558 : {
559 : char *exchange_url;
560 :
561 : (void) args;
562 6 : if (GNUNET_YES == GNUNET_is_zero (&auditor_pub))
563 : {
564 : /* private key not available, try configuration for public key */
565 : char *auditor_public_key_str;
566 :
567 0 : if (GNUNET_OK !=
568 0 : GNUNET_CONFIGURATION_get_value_string (kcfg,
569 : "auditor",
570 : "PUBLIC_KEY",
571 : &auditor_public_key_str))
572 : {
573 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
574 : "auditor",
575 : "PUBLIC_KEY");
576 0 : global_ret = EXIT_NOTCONFIGURED;
577 0 : test_shutdown ();
578 0 : return;
579 : }
580 0 : if (GNUNET_OK !=
581 0 : GNUNET_CRYPTO_eddsa_public_key_from_string (
582 : auditor_public_key_str,
583 : strlen (auditor_public_key_str),
584 : &auditor_pub.eddsa_pub))
585 : {
586 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
587 : "auditor",
588 : "PUBLIC_KEY",
589 : "invalid key");
590 0 : GNUNET_free (auditor_public_key_str);
591 0 : global_ret = EXIT_NOTCONFIGURED;
592 0 : test_shutdown ();
593 0 : return;
594 : }
595 0 : GNUNET_free (auditor_public_key_str);
596 : }
597 6 : if (NULL != in)
598 : {
599 0 : fprintf (stderr,
600 : "Downloaded data was not consumed, refusing upload\n");
601 0 : test_shutdown ();
602 0 : global_ret = EXIT_FAILURE;
603 0 : return;
604 : }
605 6 : if (NULL == out)
606 : {
607 : json_error_t err;
608 :
609 0 : out = json_loadf (stdin,
610 : JSON_REJECT_DUPLICATES,
611 : &err);
612 0 : if (NULL == out)
613 : {
614 0 : fprintf (stderr,
615 : "Failed to read JSON input: %s at %d:%s (offset: %d)\n",
616 : err.text,
617 : err.line,
618 : err.source,
619 : err.position);
620 0 : test_shutdown ();
621 0 : global_ret = EXIT_FAILURE;
622 0 : return;
623 : }
624 : }
625 6 : if (! json_is_array (out))
626 : {
627 0 : fprintf (stderr,
628 : "Error: expected JSON array for `upload` command\n");
629 0 : test_shutdown ();
630 0 : global_ret = EXIT_FAILURE;
631 0 : return;
632 : }
633 6 : if (GNUNET_OK !=
634 6 : GNUNET_CONFIGURATION_get_value_string (kcfg,
635 : "exchange",
636 : "BASE_URL",
637 : &exchange_url))
638 : {
639 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
640 : "exchange",
641 : "BASE_URL");
642 0 : global_ret = EXIT_NOTCONFIGURED;
643 0 : test_shutdown ();
644 0 : return;
645 : }
646 6 : trigger_upload (exchange_url);
647 6 : json_decref (out);
648 6 : out = NULL;
649 6 : GNUNET_free (exchange_url);
650 : }
651 :
652 :
653 : /**
654 : * Function called with information about who is auditing
655 : * a particular exchange and what keys the exchange is using.
656 : *
657 : * @param args the remaining args
658 : * @param kr response data
659 : * @param keys key data from the exchange
660 : */
661 : static void
662 6 : keys_cb (
663 : char *const *args,
664 : const struct TALER_EXCHANGE_KeysResponse *kr,
665 : struct TALER_EXCHANGE_Keys *keys)
666 : {
667 6 : exchange = NULL;
668 6 : switch (kr->hr.http_status)
669 : {
670 6 : case MHD_HTTP_OK:
671 6 : if (NULL == kr->hr.reply)
672 : {
673 0 : GNUNET_break_op (0);
674 0 : test_shutdown ();
675 0 : global_ret = EXIT_FAILURE;
676 0 : if (NULL != keys)
677 0 : TALER_EXCHANGE_keys_decref (keys);
678 0 : return;
679 : }
680 6 : break;
681 0 : default:
682 0 : fprintf (stderr,
683 : "Failed to download keys: %s (HTTP status: %u/EC: %u)\n",
684 0 : kr->hr.hint,
685 0 : kr->hr.http_status,
686 0 : (unsigned int) kr->hr.ec);
687 0 : test_shutdown ();
688 0 : global_ret = EXIT_FAILURE;
689 0 : return;
690 : }
691 6 : in = GNUNET_JSON_PACK (
692 : GNUNET_JSON_pack_string ("operation",
693 : OP_INPUT_KEYS),
694 : GNUNET_JSON_pack_object_incref ("arguments",
695 : (json_t *) kr->hr.reply));
696 6 : if (NULL == args[0])
697 : {
698 0 : json_dumpf (in,
699 : stdout,
700 : JSON_INDENT (2));
701 0 : json_decref (in);
702 0 : in = NULL;
703 : }
704 6 : next (args);
705 6 : TALER_EXCHANGE_keys_decref (keys);
706 : }
707 :
708 :
709 : /**
710 : * Download future keys.
711 : *
712 : * @param args the array of command-line arguments to process next
713 : */
714 : static void
715 6 : do_download (char *const *args)
716 : {
717 : char *exchange_url;
718 :
719 6 : if (GNUNET_OK !=
720 6 : GNUNET_CONFIGURATION_get_value_string (kcfg,
721 : "exchange",
722 : "BASE_URL",
723 : &exchange_url))
724 : {
725 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
726 : "exchange",
727 : "BASE_URL");
728 0 : test_shutdown ();
729 0 : global_ret = EXIT_NOTCONFIGURED;
730 0 : return;
731 : }
732 6 : exchange = TALER_EXCHANGE_get_keys_create (ctx,
733 : exchange_url);
734 6 : GNUNET_assert (TALER_EC_NONE ==
735 : TALER_EXCHANGE_get_keys_start (exchange,
736 : &keys_cb,
737 : args));
738 6 : GNUNET_free (exchange_url);
739 : }
740 :
741 :
742 : /**
743 : * Output @a denomkeys for human consumption.
744 : *
745 : * @param denomkeys keys to output
746 : * @return #GNUNET_OK on success
747 : */
748 : static enum GNUNET_GenericReturnValue
749 0 : show_denomkeys (const json_t *denomkeys)
750 : {
751 : size_t index;
752 : json_t *value;
753 :
754 0 : json_array_foreach (denomkeys, index, value) {
755 : struct TALER_DenominationGroup group;
756 : const json_t *denoms;
757 : const char *err_name;
758 : unsigned int err_line;
759 : struct GNUNET_JSON_Specification spec[] = {
760 0 : TALER_JSON_spec_denomination_group (NULL,
761 : currency,
762 : &group),
763 0 : GNUNET_JSON_spec_array_const ("denoms",
764 : &denoms),
765 0 : GNUNET_JSON_spec_end ()
766 : };
767 : size_t index2;
768 : json_t *value2;
769 :
770 0 : if (GNUNET_OK !=
771 0 : GNUNET_JSON_parse (value,
772 : spec,
773 : &err_name,
774 : &err_line))
775 : {
776 0 : fprintf (stderr,
777 : "Invalid input for denomination key to 'show': %s#%u at %u (skipping)\n",
778 : err_name,
779 : err_line,
780 : (unsigned int) index);
781 0 : GNUNET_JSON_parse_free (spec);
782 0 : global_ret = EXIT_FAILURE;
783 0 : test_shutdown ();
784 0 : return GNUNET_SYSERR;
785 : }
786 0 : json_array_foreach (denoms, index2, value2) {
787 : struct GNUNET_TIME_Timestamp stamp_start;
788 : struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
789 : struct GNUNET_TIME_Timestamp stamp_expire_deposit;
790 : struct GNUNET_TIME_Timestamp stamp_expire_legal;
791 : struct TALER_DenominationPublicKey denom_pub;
792 : struct TALER_MasterSignatureP master_sig;
793 : struct GNUNET_JSON_Specification ispec[] = {
794 0 : TALER_JSON_spec_denom_pub_cipher (NULL,
795 : group.cipher,
796 : &denom_pub),
797 0 : GNUNET_JSON_spec_timestamp ("stamp_start",
798 : &stamp_start),
799 0 : GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
800 : &stamp_expire_withdraw),
801 0 : GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
802 : &stamp_expire_deposit),
803 0 : GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
804 : &stamp_expire_legal),
805 0 : GNUNET_JSON_spec_fixed_auto ("master_sig",
806 : &master_sig),
807 0 : GNUNET_JSON_spec_end ()
808 : };
809 : struct GNUNET_TIME_Relative duration;
810 : struct TALER_DenominationHashP h_denom_pub;
811 :
812 0 : if (GNUNET_OK !=
813 0 : GNUNET_JSON_parse (value2,
814 : ispec,
815 : &err_name,
816 : &err_line))
817 : {
818 0 : fprintf (stderr,
819 : "Invalid input for denomination key to 'show': %s#%u at %u/%u (skipping)\n",
820 : err_name,
821 : err_line,
822 : (unsigned int) index,
823 : (unsigned int) index2);
824 0 : GNUNET_JSON_parse_free (spec);
825 0 : global_ret = EXIT_FAILURE;
826 0 : test_shutdown ();
827 0 : return GNUNET_SYSERR;
828 : }
829 0 : duration = GNUNET_TIME_absolute_get_difference (
830 : stamp_start.abs_time,
831 : stamp_expire_withdraw.abs_time);
832 0 : TALER_denom_pub_hash (&denom_pub,
833 : &h_denom_pub);
834 0 : if (GNUNET_OK !=
835 0 : TALER_exchange_offline_denom_validity_verify (
836 : &h_denom_pub,
837 : stamp_start,
838 : stamp_expire_withdraw,
839 : stamp_expire_deposit,
840 : stamp_expire_legal,
841 : &group.value,
842 : &group.fees,
843 : &master_pub,
844 : &master_sig))
845 : {
846 0 : fprintf (stderr,
847 : "Invalid master signature for key %s (aborting)\n",
848 : TALER_B2S (&h_denom_pub));
849 0 : global_ret = EXIT_FAILURE;
850 0 : GNUNET_JSON_parse_free (ispec);
851 0 : GNUNET_JSON_parse_free (spec);
852 0 : test_shutdown ();
853 0 : return GNUNET_SYSERR;
854 : }
855 :
856 : {
857 : char *withdraw_fee_s;
858 : char *deposit_fee_s;
859 : char *refresh_fee_s;
860 : char *refund_fee_s;
861 : char *deposit_s;
862 : char *legal_s;
863 :
864 0 : withdraw_fee_s = TALER_amount_to_string (&group.fees.withdraw);
865 0 : deposit_fee_s = TALER_amount_to_string (&group.fees.deposit);
866 0 : refresh_fee_s = TALER_amount_to_string (&group.fees.refresh);
867 0 : refund_fee_s = TALER_amount_to_string (&group.fees.refund);
868 0 : deposit_s = GNUNET_strdup (
869 : GNUNET_TIME_timestamp2s (stamp_expire_deposit));
870 0 : legal_s = GNUNET_strdup (
871 : GNUNET_TIME_timestamp2s (stamp_expire_legal));
872 :
873 0 : printf (
874 : "DENOMINATION-KEY %s of value %s starting at %s "
875 : "(used for: %s, deposit until: %s legal end: %s) with fees %s/%s/%s/%s\n",
876 : TALER_B2S (&h_denom_pub),
877 : TALER_amount2s (&group.value),
878 : GNUNET_TIME_timestamp2s (stamp_start),
879 : GNUNET_TIME_relative2s (duration,
880 : false),
881 : deposit_s,
882 : legal_s,
883 : withdraw_fee_s,
884 : deposit_fee_s,
885 : refresh_fee_s,
886 : refund_fee_s);
887 0 : GNUNET_free (withdraw_fee_s);
888 0 : GNUNET_free (deposit_fee_s);
889 0 : GNUNET_free (refresh_fee_s);
890 0 : GNUNET_free (refund_fee_s);
891 0 : GNUNET_free (deposit_s);
892 0 : GNUNET_free (legal_s);
893 : }
894 0 : GNUNET_JSON_parse_free (ispec);
895 : }
896 0 : GNUNET_JSON_parse_free (spec);
897 : }
898 0 : return GNUNET_OK;
899 : }
900 :
901 :
902 : /**
903 : * Parse the '/keys' input for operation called @a command_name.
904 : *
905 : * @param command_name name of the command, for logging errors
906 : * @return NULL if the input is malformed
907 : */
908 : static json_t *
909 6 : parse_keys (const char *command_name)
910 : {
911 : json_t *keys;
912 : const char *op_str;
913 : struct GNUNET_JSON_Specification spec[] = {
914 6 : GNUNET_JSON_spec_json ("arguments",
915 : &keys),
916 6 : GNUNET_JSON_spec_string ("operation",
917 : &op_str),
918 6 : GNUNET_JSON_spec_end ()
919 : };
920 : const char *err_name;
921 : unsigned int err_line;
922 :
923 6 : if (NULL == in)
924 : {
925 : json_error_t err;
926 :
927 0 : in = json_loadf (stdin,
928 : JSON_REJECT_DUPLICATES,
929 : &err);
930 0 : if (NULL == in)
931 : {
932 0 : fprintf (stderr,
933 : "Failed to read JSON input: %s at %d:%s (offset: %d)\n",
934 : err.text,
935 : err.line,
936 : err.source,
937 : err.position);
938 0 : global_ret = EXIT_FAILURE;
939 0 : test_shutdown ();
940 0 : return NULL;
941 : }
942 : }
943 6 : if (GNUNET_OK !=
944 6 : GNUNET_JSON_parse (in,
945 : spec,
946 : &err_name,
947 : &err_line))
948 : {
949 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
950 : "Invalid input to '%s': %s#%u (skipping)\n",
951 : command_name,
952 : err_name,
953 : err_line);
954 0 : json_dumpf (in,
955 : stderr,
956 : JSON_INDENT (2));
957 0 : global_ret = EXIT_FAILURE;
958 0 : test_shutdown ();
959 0 : return NULL;
960 : }
961 6 : if (0 != strcmp (op_str,
962 : OP_INPUT_KEYS))
963 : {
964 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
965 : "Invalid input to '%s' : operation is `%s', expected `%s'\n",
966 : command_name,
967 : op_str,
968 : OP_INPUT_KEYS);
969 0 : GNUNET_JSON_parse_free (spec);
970 0 : global_ret = EXIT_FAILURE;
971 0 : test_shutdown ();
972 0 : return NULL;
973 : }
974 6 : json_decref (in);
975 6 : in = NULL;
976 6 : return keys;
977 : }
978 :
979 :
980 : /**
981 : * Show exchange denomination keys.
982 : *
983 : * @param args the array of command-line arguments to process next
984 : */
985 : static void
986 0 : do_show (char *const *args)
987 : {
988 : json_t *keys;
989 : const char *err_name;
990 : unsigned int err_line;
991 : const json_t *denomkeys;
992 : struct TALER_MasterPublicKeyP mpub;
993 : struct GNUNET_JSON_Specification spec[] = {
994 0 : GNUNET_JSON_spec_array_const ("denominations",
995 : &denomkeys),
996 0 : GNUNET_JSON_spec_fixed_auto ("master_public_key",
997 : &mpub),
998 0 : GNUNET_JSON_spec_end ()
999 : };
1000 :
1001 0 : keys = parse_keys ("show");
1002 0 : if (NULL == keys)
1003 : {
1004 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1005 : "Showing failed: no valid input\n");
1006 0 : return;
1007 : }
1008 0 : if (GNUNET_OK !=
1009 0 : GNUNET_JSON_parse (keys,
1010 : spec,
1011 : &err_name,
1012 : &err_line))
1013 : {
1014 0 : fprintf (stderr,
1015 : "Invalid input to 'show': %s#%u (skipping)\n",
1016 : err_name,
1017 : err_line);
1018 0 : global_ret = EXIT_FAILURE;
1019 0 : test_shutdown ();
1020 0 : json_decref (keys);
1021 0 : return;
1022 : }
1023 0 : if (0 !=
1024 0 : GNUNET_memcmp (&mpub,
1025 : &master_pub))
1026 : {
1027 0 : fprintf (stderr,
1028 : "Exchange master public key does not match key we have configured (aborting)\n");
1029 0 : global_ret = EXIT_FAILURE;
1030 0 : test_shutdown ();
1031 0 : json_decref (keys);
1032 0 : return;
1033 : }
1034 0 : if (GNUNET_OK !=
1035 0 : show_denomkeys (denomkeys))
1036 : {
1037 0 : global_ret = EXIT_FAILURE;
1038 0 : test_shutdown ();
1039 0 : json_decref (keys);
1040 0 : return;
1041 : }
1042 0 : json_decref (keys);
1043 : /* do NOT consume input if next argument is '-' */
1044 0 : if ( (NULL != args[0]) &&
1045 0 : (0 == strcmp ("-",
1046 : args[0])) )
1047 : {
1048 0 : next (args + 1);
1049 0 : return;
1050 : }
1051 0 : next (args);
1052 : }
1053 :
1054 :
1055 : /**
1056 : * Sign @a denomkeys with offline key.
1057 : *
1058 : * @param denomkeys keys to output
1059 : * @return #GNUNET_OK on success
1060 : */
1061 : static enum GNUNET_GenericReturnValue
1062 6 : sign_denomkeys (const json_t *denomkeys)
1063 : {
1064 : size_t group_idx;
1065 : json_t *value;
1066 :
1067 66 : json_array_foreach (denomkeys, group_idx, value) {
1068 60 : struct TALER_DenominationGroup group = { 0 };
1069 : const json_t *denom_keys_array;
1070 : const char *err_name;
1071 : unsigned int err_line;
1072 : struct GNUNET_JSON_Specification spec[] = {
1073 60 : TALER_JSON_spec_denomination_group (NULL,
1074 : currency,
1075 : &group),
1076 60 : GNUNET_JSON_spec_array_const ("denoms",
1077 : &denom_keys_array),
1078 60 : GNUNET_JSON_spec_end ()
1079 : };
1080 : size_t index;
1081 : json_t *denom_key_obj;
1082 :
1083 60 : if (GNUNET_OK !=
1084 60 : GNUNET_JSON_parse (value,
1085 : spec,
1086 : &err_name,
1087 : &err_line))
1088 : {
1089 0 : fprintf (stderr,
1090 : "Invalid input for denomination key to 'sign': %s#%u at %u (skipping)\n",
1091 : err_name,
1092 : err_line,
1093 : (unsigned int) group_idx);
1094 0 : GNUNET_JSON_parse_free (spec);
1095 0 : global_ret = EXIT_FAILURE;
1096 0 : test_shutdown ();
1097 0 : return GNUNET_SYSERR;
1098 : }
1099 300 : json_array_foreach (denom_keys_array, index, denom_key_obj) {
1100 : struct GNUNET_TIME_Timestamp stamp_start;
1101 : struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
1102 : struct GNUNET_TIME_Timestamp stamp_expire_deposit;
1103 : struct GNUNET_TIME_Timestamp stamp_expire_legal;
1104 240 : struct TALER_DenominationPublicKey denom_pub = {
1105 : .age_mask = group.age_mask
1106 : };
1107 : struct TALER_MasterSignatureP master_sig;
1108 : struct GNUNET_JSON_Specification ispec[] = {
1109 240 : TALER_JSON_spec_denom_pub_cipher (NULL,
1110 : group.cipher,
1111 : &denom_pub),
1112 240 : GNUNET_JSON_spec_timestamp ("stamp_start",
1113 : &stamp_start),
1114 240 : GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
1115 : &stamp_expire_withdraw),
1116 240 : GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
1117 : &stamp_expire_deposit),
1118 240 : GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
1119 : &stamp_expire_legal),
1120 240 : GNUNET_JSON_spec_fixed_auto ("master_sig",
1121 : &master_sig),
1122 240 : GNUNET_JSON_spec_end ()
1123 : };
1124 : struct TALER_DenominationHashP h_denom_pub;
1125 :
1126 240 : if (GNUNET_OK !=
1127 240 : GNUNET_JSON_parse (denom_key_obj,
1128 : ispec,
1129 : &err_name,
1130 : &err_line))
1131 : {
1132 0 : fprintf (stderr,
1133 : "Invalid input for denomination key to 'sign': %s#%u at %u/%u (skipping)\n",
1134 : err_name,
1135 : err_line,
1136 : (unsigned int) group_idx,
1137 : (unsigned int) index);
1138 0 : GNUNET_JSON_parse_free (spec);
1139 0 : global_ret = EXIT_FAILURE;
1140 0 : test_shutdown ();
1141 0 : return GNUNET_SYSERR;
1142 : }
1143 240 : TALER_denom_pub_hash (&denom_pub,
1144 : &h_denom_pub);
1145 240 : if (GNUNET_OK !=
1146 240 : TALER_exchange_offline_denom_validity_verify (
1147 : &h_denom_pub,
1148 : stamp_start,
1149 : stamp_expire_withdraw,
1150 : stamp_expire_deposit,
1151 : stamp_expire_legal,
1152 : &group.value,
1153 : &group.fees,
1154 : &master_pub,
1155 : &master_sig))
1156 : {
1157 0 : fprintf (stderr,
1158 : "Invalid master signature for key %s (aborting)\n",
1159 : TALER_B2S (&h_denom_pub));
1160 0 : global_ret = EXIT_FAILURE;
1161 0 : test_shutdown ();
1162 0 : return GNUNET_SYSERR;
1163 : }
1164 :
1165 : {
1166 : struct TALER_AuditorSignatureP auditor_sig;
1167 :
1168 240 : TALER_auditor_denom_validity_sign (auditor_url,
1169 : &h_denom_pub,
1170 : &master_pub,
1171 : stamp_start,
1172 : stamp_expire_withdraw,
1173 : stamp_expire_deposit,
1174 : stamp_expire_legal,
1175 : &group.value,
1176 : &group.fees,
1177 : &auditor_priv,
1178 : &auditor_sig);
1179 240 : output_operation (OP_SIGN_DENOMINATION,
1180 240 : GNUNET_JSON_PACK (
1181 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
1182 : &h_denom_pub),
1183 : GNUNET_JSON_pack_data_auto ("auditor_sig",
1184 : &auditor_sig)));
1185 : }
1186 240 : GNUNET_JSON_parse_free (ispec);
1187 : }
1188 60 : GNUNET_JSON_parse_free (spec);
1189 : }
1190 6 : return GNUNET_OK;
1191 : }
1192 :
1193 :
1194 : /**
1195 : * Sign denomination keys.
1196 : *
1197 : * @param args the array of command-line arguments to process next
1198 : */
1199 : static void
1200 6 : do_sign (char *const *args)
1201 : {
1202 : json_t *keys;
1203 : const char *err_name;
1204 : unsigned int err_line;
1205 : struct TALER_MasterPublicKeyP mpub;
1206 : const json_t *denomkeys;
1207 : struct GNUNET_JSON_Specification spec[] = {
1208 6 : GNUNET_JSON_spec_array_const ("denominations",
1209 : &denomkeys),
1210 6 : GNUNET_JSON_spec_fixed_auto ("master_public_key",
1211 : &mpub),
1212 6 : GNUNET_JSON_spec_end ()
1213 : };
1214 :
1215 6 : keys = parse_keys ("sign");
1216 6 : if (NULL == keys)
1217 : {
1218 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1219 : "Signing failed: no valid input\n");
1220 0 : return;
1221 : }
1222 6 : if (GNUNET_OK !=
1223 6 : load_offline_key (GNUNET_NO))
1224 : {
1225 0 : json_decref (keys);
1226 0 : return;
1227 : }
1228 6 : if (GNUNET_OK !=
1229 6 : GNUNET_JSON_parse (keys,
1230 : spec,
1231 : &err_name,
1232 : &err_line))
1233 : {
1234 0 : fprintf (stderr,
1235 : "Invalid input to 'sign': %s#%u (skipping)\n",
1236 : err_name,
1237 : err_line);
1238 0 : global_ret = EXIT_FAILURE;
1239 0 : test_shutdown ();
1240 0 : json_decref (keys);
1241 0 : return;
1242 : }
1243 6 : if (0 !=
1244 6 : GNUNET_memcmp (&mpub,
1245 : &master_pub))
1246 : {
1247 0 : fprintf (stderr,
1248 : "Exchange master public key does not match key we have configured (aborting)\n");
1249 0 : global_ret = EXIT_FAILURE;
1250 0 : test_shutdown ();
1251 0 : json_decref (keys);
1252 0 : return;
1253 : }
1254 6 : if (NULL == out)
1255 : {
1256 6 : out = json_array ();
1257 6 : GNUNET_assert (NULL != out);
1258 : }
1259 6 : if (GNUNET_OK !=
1260 6 : sign_denomkeys (denomkeys))
1261 : {
1262 0 : global_ret = EXIT_FAILURE;
1263 0 : test_shutdown ();
1264 0 : json_decref (keys);
1265 0 : return;
1266 : }
1267 6 : json_decref (keys);
1268 6 : next (args);
1269 : }
1270 :
1271 :
1272 : /**
1273 : * Setup and output offline signing key.
1274 : *
1275 : * @param args the array of command-line arguments to process next
1276 : */
1277 : static void
1278 0 : do_setup (char *const *args)
1279 : {
1280 0 : if (GNUNET_OK !=
1281 0 : load_offline_key (GNUNET_YES))
1282 : {
1283 0 : global_ret = EXIT_FAILURE;
1284 0 : return;
1285 : }
1286 0 : if (NULL != *args)
1287 : {
1288 0 : if (NULL == out)
1289 : {
1290 0 : out = json_array ();
1291 0 : GNUNET_assert (NULL != out);
1292 : }
1293 0 : output_operation (OP_SETUP,
1294 0 : GNUNET_JSON_PACK (
1295 : GNUNET_JSON_pack_data_auto ("auditor_pub",
1296 : &auditor_pub)));
1297 : }
1298 :
1299 : else
1300 : {
1301 : char *pub_s;
1302 :
1303 0 : pub_s = GNUNET_STRINGS_data_to_string_alloc (&auditor_pub,
1304 : sizeof (auditor_pub));
1305 0 : fprintf (stdout,
1306 : "%s\n",
1307 : pub_s);
1308 0 : GNUNET_free (pub_s);
1309 : }
1310 0 : if ( (NULL != *args) &&
1311 0 : (0 == strcmp (*args,
1312 : "-")) )
1313 0 : args++;
1314 0 : next (args);
1315 : }
1316 :
1317 :
1318 : static void
1319 18 : work (void *cls)
1320 : {
1321 18 : char *const *args = cls;
1322 18 : struct SubCommand cmds[] = {
1323 : {
1324 : .name = "setup",
1325 : .help =
1326 : "setup auditor offline private key and show the public key",
1327 : .cb = &do_setup
1328 : },
1329 : {
1330 : .name = "download",
1331 : .help =
1332 : "obtain keys from exchange (to be performed online!)",
1333 : .cb = &do_download
1334 : },
1335 : {
1336 : .name = "show",
1337 : .help =
1338 : "display keys from exchange for human review (pass '-' as argument to disable consuming input)",
1339 : .cb = &do_show
1340 : },
1341 : {
1342 : .name = "sign",
1343 : .help =
1344 : "sign all denomination keys from the input",
1345 : .cb = &do_sign
1346 : },
1347 : {
1348 : .name = "upload",
1349 : .help =
1350 : "upload operation result to exchange (to be performed online!)",
1351 : .cb = &do_upload
1352 : },
1353 : /* list terminator */
1354 : {
1355 : .name = NULL,
1356 : }
1357 : };
1358 : (void) cls;
1359 :
1360 18 : nxt = NULL;
1361 66 : for (unsigned int i = 0; NULL != cmds[i].name; i++)
1362 : {
1363 66 : if (0 == strcasecmp (cmds[i].name,
1364 : args[0]))
1365 : {
1366 18 : cmds[i].cb (&args[1]);
1367 18 : return;
1368 : }
1369 : }
1370 :
1371 0 : if (0 != strcasecmp ("help",
1372 : args[0]))
1373 : {
1374 0 : fprintf (stderr,
1375 : "Unexpected command `%s'\n",
1376 : args[0]);
1377 0 : global_ret = EXIT_INVALIDARGUMENT;
1378 : }
1379 0 : fprintf (stderr,
1380 : "Supported subcommands:\n");
1381 0 : for (unsigned int i = 0; NULL != cmds[i].name; i++)
1382 : {
1383 0 : fprintf (stderr,
1384 : "\t%s - %s\n",
1385 : cmds[i].name,
1386 : cmds[i].help);
1387 : }
1388 : }
1389 :
1390 :
1391 : /**
1392 : * Main function that will be run.
1393 : *
1394 : * @param cls closure
1395 : * @param args remaining command-line arguments
1396 : * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1397 : * @param cfg configuration
1398 : */
1399 : static void
1400 6 : run (void *cls,
1401 : char *const *args,
1402 : const char *cfgfile,
1403 : const struct GNUNET_CONFIGURATION_Handle *cfg)
1404 : {
1405 : (void) cls;
1406 : (void) cfgfile;
1407 6 : kcfg = cfg;
1408 6 : if (GNUNET_OK !=
1409 6 : TALER_config_get_currency (kcfg,
1410 : "exchange",
1411 : ¤cy))
1412 : {
1413 0 : global_ret = EXIT_NOTCONFIGURED;
1414 0 : return;
1415 : }
1416 6 : if (GNUNET_OK !=
1417 6 : GNUNET_CONFIGURATION_get_value_string (kcfg,
1418 : "auditor",
1419 : "BASE_URL",
1420 : &auditor_url))
1421 : {
1422 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1423 : "auditor",
1424 : "BASE_URL");
1425 0 : global_ret = EXIT_NOTCONFIGURED;
1426 0 : return;
1427 : }
1428 : {
1429 : char *master_public_key_str;
1430 :
1431 6 : if (GNUNET_OK !=
1432 6 : GNUNET_CONFIGURATION_get_value_string (cfg,
1433 : "exchange",
1434 : "MASTER_PUBLIC_KEY",
1435 : &master_public_key_str))
1436 : {
1437 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1438 : "exchange",
1439 : "MASTER_PUBLIC_KEY");
1440 0 : global_ret = EXIT_NOTCONFIGURED;
1441 0 : return;
1442 : }
1443 6 : if (GNUNET_OK !=
1444 6 : GNUNET_CRYPTO_eddsa_public_key_from_string (
1445 : master_public_key_str,
1446 : strlen (master_public_key_str),
1447 : &master_pub.eddsa_pub))
1448 : {
1449 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1450 : "Invalid master public key given in exchange configuration.");
1451 0 : GNUNET_free (master_public_key_str);
1452 0 : global_ret = EXIT_NOTCONFIGURED;
1453 0 : return;
1454 : }
1455 6 : GNUNET_free (master_public_key_str);
1456 : }
1457 6 : ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
1458 : &rc);
1459 6 : rc = GNUNET_CURL_gnunet_rc_create (ctx);
1460 6 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1461 : NULL);
1462 6 : next (args);
1463 : }
1464 :
1465 :
1466 : /**
1467 : * The main function of the taler-auditor-offline tool. This tool is used to
1468 : * sign denomination keys with the auditor's key. It uses the long-term
1469 : * offline private key of the auditor and generates signatures with it. It
1470 : * also supports online operations with the exchange to download its input
1471 : * data and to upload its results. Those online operations should be performed
1472 : * on another machine in production!
1473 : *
1474 : * @param argc number of arguments from the command line
1475 : * @param argv command line arguments
1476 : * @return 0 ok, 1 on error
1477 : */
1478 : int
1479 6 : main (int argc,
1480 : char *const *argv)
1481 : {
1482 6 : struct GNUNET_GETOPT_CommandLineOption options[] = {
1483 : GNUNET_GETOPT_OPTION_END
1484 : };
1485 : enum GNUNET_GenericReturnValue ret;
1486 :
1487 6 : ret = GNUNET_PROGRAM_run (
1488 : TALER_AUDITOR_project_data (),
1489 : argc, argv,
1490 : "taler-auditor-offline",
1491 : gettext_noop ("Operations for offline signing for a Taler exchange"),
1492 : options,
1493 : &run, NULL);
1494 6 : if (GNUNET_SYSERR == ret)
1495 0 : return EXIT_INVALIDARGUMENT;
1496 6 : if (GNUNET_NO == ret)
1497 0 : return EXIT_SUCCESS;
1498 6 : return global_ret;
1499 : }
1500 :
1501 :
1502 : /* end of taler-auditor-offline.c */
|