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

Generated by: LCOV version 2.0-1