LCOV - code coverage report
Current view: top level - exchange-tools - taler-auditor-offline.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 40.9 % 452 185
Test Date: 2026-03-10 12:10:57 Functions: 85.0 % 20 17

            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              :                                  &currency))
    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 */
        

Generated by: LCOV version 2.0-1