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

Generated by: LCOV version 2.0-1