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 "taler/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/taler-exchange/get-keys.h"
29 :
30 : struct DenominationAddRequest;
31 : #define TALER_EXCHANGE_POST_AUDITORS_RESULT_CLOSURE \
32 : struct DenominationAddRequest
33 : #include "taler/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 : * Show 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 wire 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 : "Darning: 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 denomination revocation
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 (0);
674 0 : test_shutdown ();
675 0 : global_ret = EXIT_FAILURE;
676 0 : return;
677 : }
678 6 : break;
679 0 : default:
680 0 : fprintf (stderr,
681 : "Failed to download keys: %s (HTTP status: %u/%u)\n",
682 0 : kr->hr.hint,
683 0 : kr->hr.http_status,
684 0 : (unsigned int) kr->hr.ec);
685 0 : test_shutdown ();
686 0 : global_ret = EXIT_FAILURE;
687 0 : return;
688 : }
689 6 : in = GNUNET_JSON_PACK (
690 : GNUNET_JSON_pack_string ("operation",
691 : OP_INPUT_KEYS),
692 : GNUNET_JSON_pack_object_incref ("arguments",
693 : (json_t *) kr->hr.reply));
694 6 : if (NULL == args[0])
695 : {
696 0 : json_dumpf (in,
697 : stdout,
698 : JSON_INDENT (2));
699 0 : json_decref (in);
700 0 : in = NULL;
701 : }
702 6 : next (args);
703 6 : TALER_EXCHANGE_keys_decref (keys);
704 : }
705 :
706 :
707 : /**
708 : * Download future keys.
709 : *
710 : * @param args the array of command-line arguments to process next
711 : */
712 : static void
713 6 : do_download (char *const *args)
714 : {
715 : char *exchange_url;
716 :
717 6 : if (GNUNET_OK !=
718 6 : GNUNET_CONFIGURATION_get_value_string (kcfg,
719 : "exchange",
720 : "BASE_URL",
721 : &exchange_url))
722 : {
723 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
724 : "exchange",
725 : "BASE_URL");
726 0 : test_shutdown ();
727 0 : global_ret = EXIT_NOTCONFIGURED;
728 0 : return;
729 : }
730 6 : exchange = TALER_EXCHANGE_get_keys_create (ctx,
731 : exchange_url);
732 6 : TALER_EXCHANGE_get_keys_start (exchange,
733 : &keys_cb,
734 : args);
735 6 : GNUNET_free (exchange_url);
736 : }
737 :
738 :
739 : /**
740 : * Output @a denomkeys for human consumption.
741 : *
742 : * @param denomkeys keys to output
743 : * @return #GNUNET_OK on success
744 : */
745 : static enum GNUNET_GenericReturnValue
746 0 : show_denomkeys (const json_t *denomkeys)
747 : {
748 : size_t index;
749 : json_t *value;
750 :
751 0 : json_array_foreach (denomkeys, index, value) {
752 : struct TALER_DenominationGroup group;
753 : const json_t *denoms;
754 : const char *err_name;
755 : unsigned int err_line;
756 : struct GNUNET_JSON_Specification spec[] = {
757 0 : TALER_JSON_spec_denomination_group (NULL,
758 : currency,
759 : &group),
760 0 : GNUNET_JSON_spec_array_const ("denoms",
761 : &denoms),
762 0 : GNUNET_JSON_spec_end ()
763 : };
764 : size_t index2;
765 : json_t *value2;
766 :
767 0 : if (GNUNET_OK !=
768 0 : GNUNET_JSON_parse (value,
769 : spec,
770 : &err_name,
771 : &err_line))
772 : {
773 0 : fprintf (stderr,
774 : "Invalid input for denomination key to 'show': %s#%u at %u (skipping)\n",
775 : err_name,
776 : err_line,
777 : (unsigned int) index);
778 0 : GNUNET_JSON_parse_free (spec);
779 0 : global_ret = EXIT_FAILURE;
780 0 : test_shutdown ();
781 0 : return GNUNET_SYSERR;
782 : }
783 0 : json_array_foreach (denoms, index2, value2) {
784 : struct GNUNET_TIME_Timestamp stamp_start;
785 : struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
786 : struct GNUNET_TIME_Timestamp stamp_expire_deposit;
787 : struct GNUNET_TIME_Timestamp stamp_expire_legal;
788 : struct TALER_DenominationPublicKey denom_pub;
789 : struct TALER_MasterSignatureP master_sig;
790 : struct GNUNET_JSON_Specification ispec[] = {
791 0 : TALER_JSON_spec_denom_pub_cipher (NULL,
792 : group.cipher,
793 : &denom_pub),
794 0 : GNUNET_JSON_spec_timestamp ("stamp_start",
795 : &stamp_start),
796 0 : GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
797 : &stamp_expire_withdraw),
798 0 : GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
799 : &stamp_expire_deposit),
800 0 : GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
801 : &stamp_expire_legal),
802 0 : GNUNET_JSON_spec_fixed_auto ("master_sig",
803 : &master_sig),
804 0 : GNUNET_JSON_spec_end ()
805 : };
806 : struct GNUNET_TIME_Relative duration;
807 : struct TALER_DenominationHashP h_denom_pub;
808 :
809 0 : if (GNUNET_OK !=
810 0 : GNUNET_JSON_parse (value2,
811 : ispec,
812 : &err_name,
813 : &err_line))
814 : {
815 0 : fprintf (stderr,
816 : "Invalid input for denomination key to 'show': %s#%u at %u/%u (skipping)\n",
817 : err_name,
818 : err_line,
819 : (unsigned int) index,
820 : (unsigned int) index2);
821 0 : GNUNET_JSON_parse_free (spec);
822 0 : global_ret = EXIT_FAILURE;
823 0 : test_shutdown ();
824 0 : return GNUNET_SYSERR;
825 : }
826 0 : duration = GNUNET_TIME_absolute_get_difference (
827 : stamp_start.abs_time,
828 : stamp_expire_withdraw.abs_time);
829 0 : TALER_denom_pub_hash (&denom_pub,
830 : &h_denom_pub);
831 0 : if (GNUNET_OK !=
832 0 : TALER_exchange_offline_denom_validity_verify (
833 : &h_denom_pub,
834 : stamp_start,
835 : stamp_expire_withdraw,
836 : stamp_expire_deposit,
837 : stamp_expire_legal,
838 : &group.value,
839 : &group.fees,
840 : &master_pub,
841 : &master_sig))
842 : {
843 0 : fprintf (stderr,
844 : "Invalid master signature for key %s (aborting)\n",
845 : TALER_B2S (&h_denom_pub));
846 0 : global_ret = EXIT_FAILURE;
847 0 : GNUNET_JSON_parse_free (ispec);
848 0 : GNUNET_JSON_parse_free (spec);
849 0 : test_shutdown ();
850 0 : return GNUNET_SYSERR;
851 : }
852 :
853 : {
854 : char *withdraw_fee_s;
855 : char *deposit_fee_s;
856 : char *refresh_fee_s;
857 : char *refund_fee_s;
858 : char *deposit_s;
859 : char *legal_s;
860 :
861 0 : withdraw_fee_s = TALER_amount_to_string (&group.fees.withdraw);
862 0 : deposit_fee_s = TALER_amount_to_string (&group.fees.deposit);
863 0 : refresh_fee_s = TALER_amount_to_string (&group.fees.refresh);
864 0 : refund_fee_s = TALER_amount_to_string (&group.fees.refund);
865 0 : deposit_s = GNUNET_strdup (
866 : GNUNET_TIME_timestamp2s (stamp_expire_deposit));
867 0 : legal_s = GNUNET_strdup (
868 : GNUNET_TIME_timestamp2s (stamp_expire_legal));
869 :
870 0 : printf (
871 : "DENOMINATION-KEY %s of value %s starting at %s "
872 : "(used for: %s, deposit until: %s legal end: %s) with fees %s/%s/%s/%s\n",
873 : TALER_B2S (&h_denom_pub),
874 : TALER_amount2s (&group.value),
875 : GNUNET_TIME_timestamp2s (stamp_start),
876 : GNUNET_TIME_relative2s (duration,
877 : false),
878 : deposit_s,
879 : legal_s,
880 : withdraw_fee_s,
881 : deposit_fee_s,
882 : refresh_fee_s,
883 : refund_fee_s);
884 0 : GNUNET_free (withdraw_fee_s);
885 0 : GNUNET_free (deposit_fee_s);
886 0 : GNUNET_free (refresh_fee_s);
887 0 : GNUNET_free (refund_fee_s);
888 0 : GNUNET_free (deposit_s);
889 0 : GNUNET_free (legal_s);
890 : }
891 0 : GNUNET_JSON_parse_free (ispec);
892 : }
893 0 : GNUNET_JSON_parse_free (spec);
894 : }
895 0 : return GNUNET_OK;
896 : }
897 :
898 :
899 : /**
900 : * Parse the '/keys' input for operation called @a command_name.
901 : *
902 : * @param command_name name of the command, for logging errors
903 : * @return NULL if the input is malformed
904 : */
905 : static json_t *
906 6 : parse_keys (const char *command_name)
907 : {
908 : json_t *keys;
909 : const char *op_str;
910 : struct GNUNET_JSON_Specification spec[] = {
911 6 : GNUNET_JSON_spec_json ("arguments",
912 : &keys),
913 6 : GNUNET_JSON_spec_string ("operation",
914 : &op_str),
915 6 : GNUNET_JSON_spec_end ()
916 : };
917 : const char *err_name;
918 : unsigned int err_line;
919 :
920 6 : if (NULL == in)
921 : {
922 : json_error_t err;
923 :
924 0 : in = json_loadf (stdin,
925 : JSON_REJECT_DUPLICATES,
926 : &err);
927 0 : if (NULL == in)
928 : {
929 0 : fprintf (stderr,
930 : "Failed to read JSON input: %s at %d:%s (offset: %d)\n",
931 : err.text,
932 : err.line,
933 : err.source,
934 : err.position);
935 0 : global_ret = EXIT_FAILURE;
936 0 : test_shutdown ();
937 0 : return NULL;
938 : }
939 : }
940 6 : if (GNUNET_OK !=
941 6 : GNUNET_JSON_parse (in,
942 : spec,
943 : &err_name,
944 : &err_line))
945 : {
946 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
947 : "Invalid input to '%s': %s#%u (skipping)\n",
948 : command_name,
949 : err_name,
950 : err_line);
951 0 : json_dumpf (in,
952 : stderr,
953 : JSON_INDENT (2));
954 0 : global_ret = EXIT_FAILURE;
955 0 : test_shutdown ();
956 0 : return NULL;
957 : }
958 6 : if (0 != strcmp (op_str,
959 : OP_INPUT_KEYS))
960 : {
961 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
962 : "Invalid input to '%s' : operation is `%s', expected `%s'\n",
963 : command_name,
964 : op_str,
965 : OP_INPUT_KEYS);
966 0 : GNUNET_JSON_parse_free (spec);
967 0 : return NULL;
968 : }
969 6 : json_decref (in);
970 6 : in = NULL;
971 6 : return keys;
972 : }
973 :
974 :
975 : /**
976 : * Show exchange denomination keys.
977 : *
978 : * @param args the array of command-line arguments to process next
979 : */
980 : static void
981 0 : do_show (char *const *args)
982 : {
983 : json_t *keys;
984 : const char *err_name;
985 : unsigned int err_line;
986 : const json_t *denomkeys;
987 : struct TALER_MasterPublicKeyP mpub;
988 : struct GNUNET_JSON_Specification spec[] = {
989 0 : GNUNET_JSON_spec_array_const ("denominations",
990 : &denomkeys),
991 0 : GNUNET_JSON_spec_fixed_auto ("master_public_key",
992 : &mpub),
993 0 : GNUNET_JSON_spec_end ()
994 : };
995 :
996 0 : keys = parse_keys ("show");
997 0 : if (NULL == keys)
998 : {
999 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1000 : "Showing failed: no valid input\n");
1001 0 : return;
1002 : }
1003 0 : if (GNUNET_OK !=
1004 0 : GNUNET_JSON_parse (keys,
1005 : spec,
1006 : &err_name,
1007 : &err_line))
1008 : {
1009 0 : fprintf (stderr,
1010 : "Invalid input to 'show': %s#%u (skipping)\n",
1011 : err_name,
1012 : err_line);
1013 0 : global_ret = EXIT_FAILURE;
1014 0 : test_shutdown ();
1015 0 : json_decref (keys);
1016 0 : return;
1017 : }
1018 0 : if (0 !=
1019 0 : GNUNET_memcmp (&mpub,
1020 : &master_pub))
1021 : {
1022 0 : fprintf (stderr,
1023 : "Exchange master public key does not match key we have configured (aborting)\n");
1024 0 : global_ret = EXIT_FAILURE;
1025 0 : test_shutdown ();
1026 0 : json_decref (keys);
1027 0 : return;
1028 : }
1029 0 : if (GNUNET_OK !=
1030 0 : show_denomkeys (denomkeys))
1031 : {
1032 0 : global_ret = EXIT_FAILURE;
1033 0 : test_shutdown ();
1034 0 : json_decref (keys);
1035 0 : return;
1036 : }
1037 0 : json_decref (keys);
1038 : /* do NOT consume input if next argument is '-' */
1039 0 : if ( (NULL != args[0]) &&
1040 0 : (0 == strcmp ("-",
1041 : args[0])) )
1042 : {
1043 0 : next (args + 1);
1044 0 : return;
1045 : }
1046 0 : next (args);
1047 : }
1048 :
1049 :
1050 : /**
1051 : * Sign @a denomkeys with offline key.
1052 : *
1053 : * @param denomkeys keys to output
1054 : * @return #GNUNET_OK on success
1055 : */
1056 : static enum GNUNET_GenericReturnValue
1057 6 : sign_denomkeys (const json_t *denomkeys)
1058 : {
1059 : size_t group_idx;
1060 : json_t *value;
1061 :
1062 66 : json_array_foreach (denomkeys, group_idx, value) {
1063 60 : struct TALER_DenominationGroup group = { 0 };
1064 : const json_t *denom_keys_array;
1065 : const char *err_name;
1066 : unsigned int err_line;
1067 : struct GNUNET_JSON_Specification spec[] = {
1068 60 : TALER_JSON_spec_denomination_group (NULL,
1069 : currency,
1070 : &group),
1071 60 : GNUNET_JSON_spec_array_const ("denoms",
1072 : &denom_keys_array),
1073 60 : GNUNET_JSON_spec_end ()
1074 : };
1075 : size_t index;
1076 : json_t *denom_key_obj;
1077 :
1078 60 : if (GNUNET_OK !=
1079 60 : GNUNET_JSON_parse (value,
1080 : spec,
1081 : &err_name,
1082 : &err_line))
1083 : {
1084 0 : fprintf (stderr,
1085 : "Invalid input for denomination key to 'sign': %s#%u at %u (skipping)\n",
1086 : err_name,
1087 : err_line,
1088 : (unsigned int) group_idx);
1089 0 : GNUNET_JSON_parse_free (spec);
1090 0 : global_ret = EXIT_FAILURE;
1091 0 : test_shutdown ();
1092 0 : return GNUNET_SYSERR;
1093 : }
1094 300 : json_array_foreach (denom_keys_array, index, denom_key_obj) {
1095 : struct GNUNET_TIME_Timestamp stamp_start;
1096 : struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
1097 : struct GNUNET_TIME_Timestamp stamp_expire_deposit;
1098 : struct GNUNET_TIME_Timestamp stamp_expire_legal;
1099 240 : struct TALER_DenominationPublicKey denom_pub = {
1100 : .age_mask = group.age_mask
1101 : };
1102 : struct TALER_MasterSignatureP master_sig;
1103 : struct GNUNET_JSON_Specification ispec[] = {
1104 240 : TALER_JSON_spec_denom_pub_cipher (NULL,
1105 : group.cipher,
1106 : &denom_pub),
1107 240 : GNUNET_JSON_spec_timestamp ("stamp_start",
1108 : &stamp_start),
1109 240 : GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
1110 : &stamp_expire_withdraw),
1111 240 : GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
1112 : &stamp_expire_deposit),
1113 240 : GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
1114 : &stamp_expire_legal),
1115 240 : GNUNET_JSON_spec_fixed_auto ("master_sig",
1116 : &master_sig),
1117 240 : GNUNET_JSON_spec_end ()
1118 : };
1119 : struct TALER_DenominationHashP h_denom_pub;
1120 :
1121 240 : if (GNUNET_OK !=
1122 240 : GNUNET_JSON_parse (denom_key_obj,
1123 : ispec,
1124 : &err_name,
1125 : &err_line))
1126 : {
1127 0 : fprintf (stderr,
1128 : "Invalid input for denomination key to 'show': %s#%u at %u/%u (skipping)\n",
1129 : err_name,
1130 : err_line,
1131 : (unsigned int) group_idx,
1132 : (unsigned int) index);
1133 0 : GNUNET_JSON_parse_free (spec);
1134 0 : global_ret = EXIT_FAILURE;
1135 0 : test_shutdown ();
1136 0 : return GNUNET_SYSERR;
1137 : }
1138 240 : TALER_denom_pub_hash (&denom_pub,
1139 : &h_denom_pub);
1140 240 : if (GNUNET_OK !=
1141 240 : TALER_exchange_offline_denom_validity_verify (
1142 : &h_denom_pub,
1143 : stamp_start,
1144 : stamp_expire_withdraw,
1145 : stamp_expire_deposit,
1146 : stamp_expire_legal,
1147 : &group.value,
1148 : &group.fees,
1149 : &master_pub,
1150 : &master_sig))
1151 : {
1152 0 : fprintf (stderr,
1153 : "Invalid master signature for key %s (aborting)\n",
1154 : TALER_B2S (&h_denom_pub));
1155 0 : global_ret = EXIT_FAILURE;
1156 0 : test_shutdown ();
1157 0 : return GNUNET_SYSERR;
1158 : }
1159 :
1160 : {
1161 : struct TALER_AuditorSignatureP auditor_sig;
1162 :
1163 240 : TALER_auditor_denom_validity_sign (auditor_url,
1164 : &h_denom_pub,
1165 : &master_pub,
1166 : stamp_start,
1167 : stamp_expire_withdraw,
1168 : stamp_expire_deposit,
1169 : stamp_expire_legal,
1170 : &group.value,
1171 : &group.fees,
1172 : &auditor_priv,
1173 : &auditor_sig);
1174 240 : output_operation (OP_SIGN_DENOMINATION,
1175 240 : GNUNET_JSON_PACK (
1176 : GNUNET_JSON_pack_data_auto ("h_denom_pub",
1177 : &h_denom_pub),
1178 : GNUNET_JSON_pack_data_auto ("auditor_sig",
1179 : &auditor_sig)));
1180 : }
1181 240 : GNUNET_JSON_parse_free (ispec);
1182 : }
1183 60 : GNUNET_JSON_parse_free (spec);
1184 : }
1185 6 : return GNUNET_OK;
1186 : }
1187 :
1188 :
1189 : /**
1190 : * Sign denomination keys.
1191 : *
1192 : * @param args the array of command-line arguments to process next
1193 : */
1194 : static void
1195 6 : do_sign (char *const *args)
1196 : {
1197 : json_t *keys;
1198 : const char *err_name;
1199 : unsigned int err_line;
1200 : struct TALER_MasterPublicKeyP mpub;
1201 : const json_t *denomkeys;
1202 : struct GNUNET_JSON_Specification spec[] = {
1203 6 : GNUNET_JSON_spec_array_const ("denominations",
1204 : &denomkeys),
1205 6 : GNUNET_JSON_spec_fixed_auto ("master_public_key",
1206 : &mpub),
1207 6 : GNUNET_JSON_spec_end ()
1208 : };
1209 :
1210 6 : keys = parse_keys ("sign");
1211 6 : if (NULL == keys)
1212 : {
1213 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1214 : "Signing failed: no valid input\n");
1215 0 : return;
1216 : }
1217 6 : if (GNUNET_OK !=
1218 6 : load_offline_key (GNUNET_NO))
1219 : {
1220 0 : json_decref (keys);
1221 0 : return;
1222 : }
1223 6 : if (GNUNET_OK !=
1224 6 : GNUNET_JSON_parse (keys,
1225 : spec,
1226 : &err_name,
1227 : &err_line))
1228 : {
1229 0 : fprintf (stderr,
1230 : "Invalid input to 'sign': %s#%u (skipping)\n",
1231 : err_name,
1232 : err_line);
1233 0 : global_ret = EXIT_FAILURE;
1234 0 : test_shutdown ();
1235 0 : json_decref (keys);
1236 0 : return;
1237 : }
1238 6 : if (0 !=
1239 6 : GNUNET_memcmp (&mpub,
1240 : &master_pub))
1241 : {
1242 0 : fprintf (stderr,
1243 : "Exchange master public key does not match key we have configured (aborting)\n");
1244 0 : global_ret = EXIT_FAILURE;
1245 0 : test_shutdown ();
1246 0 : json_decref (keys);
1247 0 : return;
1248 : }
1249 6 : if (NULL == out)
1250 : {
1251 6 : out = json_array ();
1252 6 : GNUNET_assert (NULL != out);
1253 : }
1254 6 : if (GNUNET_OK !=
1255 6 : sign_denomkeys (denomkeys))
1256 : {
1257 0 : global_ret = EXIT_FAILURE;
1258 0 : test_shutdown ();
1259 0 : json_decref (keys);
1260 0 : return;
1261 : }
1262 6 : json_decref (keys);
1263 6 : next (args);
1264 : }
1265 :
1266 :
1267 : /**
1268 : * Setup and output offline signing key.
1269 : *
1270 : * @param args the array of command-line arguments to process next
1271 : */
1272 : static void
1273 0 : do_setup (char *const *args)
1274 : {
1275 0 : if (GNUNET_OK !=
1276 0 : load_offline_key (GNUNET_YES))
1277 : {
1278 0 : global_ret = EXIT_FAILURE;
1279 0 : return;
1280 : }
1281 0 : if (NULL != *args)
1282 : {
1283 0 : if (NULL == out)
1284 : {
1285 0 : out = json_array ();
1286 0 : GNUNET_assert (NULL != out);
1287 : }
1288 0 : output_operation (OP_SETUP,
1289 0 : GNUNET_JSON_PACK (
1290 : GNUNET_JSON_pack_data_auto ("auditor_pub",
1291 : &auditor_pub)));
1292 : }
1293 :
1294 : else
1295 : {
1296 : char *pub_s;
1297 :
1298 0 : pub_s = GNUNET_STRINGS_data_to_string_alloc (&auditor_pub,
1299 : sizeof (auditor_pub));
1300 0 : fprintf (stdout,
1301 : "%s\n",
1302 : pub_s);
1303 0 : GNUNET_free (pub_s);
1304 : }
1305 0 : if ( (NULL != *args) &&
1306 0 : (0 == strcmp (*args,
1307 : "-")) )
1308 0 : args++;
1309 0 : next (args);
1310 : }
1311 :
1312 :
1313 : static void
1314 18 : work (void *cls)
1315 : {
1316 18 : char *const *args = cls;
1317 18 : struct SubCommand cmds[] = {
1318 : {
1319 : .name = "setup",
1320 : .help =
1321 : "setup auditor offline private key and show the public key",
1322 : .cb = &do_setup
1323 : },
1324 : {
1325 : .name = "download",
1326 : .help =
1327 : "obtain keys from exchange (to be performed online!)",
1328 : .cb = &do_download
1329 : },
1330 : {
1331 : .name = "show",
1332 : .help =
1333 : "display keys from exchange for human review (pass '-' as argument to disable consuming input)",
1334 : .cb = &do_show
1335 : },
1336 : {
1337 : .name = "sign",
1338 : .help =
1339 : "sing all denomination keys from the input",
1340 : .cb = &do_sign
1341 : },
1342 : {
1343 : .name = "upload",
1344 : .help =
1345 : "upload operation result to exchange (to be performed online!)",
1346 : .cb = &do_upload
1347 : },
1348 : /* list terminator */
1349 : {
1350 : .name = NULL,
1351 : }
1352 : };
1353 : (void) cls;
1354 :
1355 18 : nxt = NULL;
1356 66 : for (unsigned int i = 0; NULL != cmds[i].name; i++)
1357 : {
1358 66 : if (0 == strcasecmp (cmds[i].name,
1359 : args[0]))
1360 : {
1361 18 : cmds[i].cb (&args[1]);
1362 18 : return;
1363 : }
1364 : }
1365 :
1366 0 : if (0 != strcasecmp ("help",
1367 : args[0]))
1368 : {
1369 0 : fprintf (stderr,
1370 : "Unexpected command `%s'\n",
1371 : args[0]);
1372 0 : global_ret = EXIT_INVALIDARGUMENT;
1373 : }
1374 0 : fprintf (stderr,
1375 : "Supported subcommands:\n");
1376 0 : for (unsigned int i = 0; NULL != cmds[i].name; i++)
1377 : {
1378 0 : fprintf (stderr,
1379 : "\t%s - %s\n",
1380 : cmds[i].name,
1381 : cmds[i].help);
1382 : }
1383 : }
1384 :
1385 :
1386 : /**
1387 : * Main function that will be run.
1388 : *
1389 : * @param cls closure
1390 : * @param args remaining command-line arguments
1391 : * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1392 : * @param cfg configuration
1393 : */
1394 : static void
1395 6 : run (void *cls,
1396 : char *const *args,
1397 : const char *cfgfile,
1398 : const struct GNUNET_CONFIGURATION_Handle *cfg)
1399 : {
1400 : (void) cls;
1401 : (void) cfgfile;
1402 6 : kcfg = cfg;
1403 6 : if (GNUNET_OK !=
1404 6 : TALER_config_get_currency (kcfg,
1405 : "exchange",
1406 : ¤cy))
1407 : {
1408 0 : global_ret = EXIT_NOTCONFIGURED;
1409 0 : return;
1410 : }
1411 6 : if (GNUNET_OK !=
1412 6 : GNUNET_CONFIGURATION_get_value_string (kcfg,
1413 : "auditor",
1414 : "BASE_URL",
1415 : &auditor_url))
1416 : {
1417 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1418 : "auditor",
1419 : "BASE_URL");
1420 0 : global_ret = EXIT_NOTCONFIGURED;
1421 0 : return;
1422 : }
1423 : {
1424 : char *master_public_key_str;
1425 :
1426 6 : if (GNUNET_OK !=
1427 6 : GNUNET_CONFIGURATION_get_value_string (cfg,
1428 : "exchange",
1429 : "MASTER_PUBLIC_KEY",
1430 : &master_public_key_str))
1431 : {
1432 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1433 : "exchange",
1434 : "MASTER_PUBLIC_KEY");
1435 0 : global_ret = EXIT_NOTCONFIGURED;
1436 0 : return;
1437 : }
1438 6 : if (GNUNET_OK !=
1439 6 : GNUNET_CRYPTO_eddsa_public_key_from_string (
1440 : master_public_key_str,
1441 : strlen (master_public_key_str),
1442 : &master_pub.eddsa_pub))
1443 : {
1444 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1445 : "Invalid master public key given in exchange configuration.");
1446 0 : GNUNET_free (master_public_key_str);
1447 0 : global_ret = EXIT_NOTCONFIGURED;
1448 0 : return;
1449 : }
1450 6 : GNUNET_free (master_public_key_str);
1451 : }
1452 6 : ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
1453 : &rc);
1454 6 : rc = GNUNET_CURL_gnunet_rc_create (ctx);
1455 6 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1456 : NULL);
1457 6 : next (args);
1458 : }
1459 :
1460 :
1461 : /**
1462 : * The main function of the taler-auditor-offline tool. This tool is used to
1463 : * sign denomination keys with the auditor's key. It uses the long-term
1464 : * offline private key of the auditor and generates signatures with it. It
1465 : * also supports online operations with the exchange to download its input
1466 : * data and to upload its results. Those online operations should be performed
1467 : * on another machine in production!
1468 : *
1469 : * @param argc number of arguments from the command line
1470 : * @param argv command line arguments
1471 : * @return 0 ok, 1 on error
1472 : */
1473 : int
1474 6 : main (int argc,
1475 : char *const *argv)
1476 : {
1477 6 : struct GNUNET_GETOPT_CommandLineOption options[] = {
1478 : GNUNET_GETOPT_OPTION_END
1479 : };
1480 : enum GNUNET_GenericReturnValue ret;
1481 :
1482 6 : ret = GNUNET_PROGRAM_run (
1483 : TALER_AUDITOR_project_data (),
1484 : argc, argv,
1485 : "taler-auditor-offline",
1486 : gettext_noop ("Operations for offline signing for a Taler exchange"),
1487 : options,
1488 : &run, NULL);
1489 6 : if (GNUNET_SYSERR == ret)
1490 0 : return EXIT_INVALIDARGUMENT;
1491 6 : if (GNUNET_NO == ret)
1492 0 : return EXIT_SUCCESS;
1493 6 : return global_ret;
1494 : }
1495 :
1496 :
1497 : /* end of taler-auditor-offline.c */
|