exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

exchange_api_management_get_keys.c (12893B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2015-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
     15   <http://www.gnu.org/licenses/>
     16 */
     17 /**
     18  * @file lib/exchange_api_management_get_keys.c
     19  * @brief functions to obtain future online keys of the exchange
     20  * @author Christian Grothoff
     21  */
     22 #include "taler/platform.h"
     23 #include "taler/taler_json_lib.h"
     24 #include <gnunet/gnunet_curl_lib.h>
     25 #include <microhttpd.h>
     26 #include "taler/taler_exchange_service.h"
     27 #include "exchange_api_curl_defaults.h"
     28 #include "taler/taler_signatures.h"
     29 #include "taler/taler_curl_lib.h"
     30 #include "taler/taler_util.h"
     31 #include "taler/taler_json_lib.h"
     32 
     33 /**
     34  * Set to 1 for extra debug logging.
     35  */
     36 #define DEBUG 0
     37 
     38 
     39 /**
     40  * @brief Handle for a GET /management/keys request.
     41  */
     42 struct TALER_EXCHANGE_ManagementGetKeysHandle
     43 {
     44 
     45   /**
     46    * The url for this request.
     47    */
     48   char *url;
     49 
     50   /**
     51    * Handle for the request.
     52    */
     53   struct GNUNET_CURL_Job *job;
     54 
     55   /**
     56    * Function to call with the result.
     57    */
     58   TALER_EXCHANGE_ManagementGetKeysCallback cb;
     59 
     60   /**
     61    * Closure for @a cb.
     62    */
     63   void *cb_cls;
     64 
     65   /**
     66    * Reference to the execution context.
     67    */
     68   struct GNUNET_CURL_Context *ctx;
     69 };
     70 
     71 
     72 /**
     73  * Handle the case that the response was of type #MHD_HTTP_OK.
     74  *
     75  * @param[in,out] gh request handle
     76  * @param response the response
     77  * @return #GNUNET_OK if the response was well-formed
     78  */
     79 static enum GNUNET_GenericReturnValue
     80 handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
     81            const json_t *response)
     82 {
     83   struct TALER_EXCHANGE_ManagementGetKeysResponse gkr = {
     84     .hr.http_status = MHD_HTTP_OK,
     85     .hr.reply = response,
     86   };
     87   struct TALER_EXCHANGE_FutureKeys *fk
     88     = &gkr.details.ok.keys;
     89   const json_t *sk;
     90   const json_t *dk;
     91   bool ok;
     92   struct GNUNET_JSON_Specification spec[] = {
     93     GNUNET_JSON_spec_array_const ("future_denoms",
     94                                   &dk),
     95     GNUNET_JSON_spec_array_const ("future_signkeys",
     96                                   &sk),
     97     GNUNET_JSON_spec_fixed_auto ("master_pub",
     98                                  &fk->master_pub),
     99     GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
    100                                  &fk->denom_secmod_public_key),
    101     GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
    102                                  &fk->denom_secmod_cs_public_key),
    103     GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
    104                                  &fk->signkey_secmod_public_key),
    105     GNUNET_JSON_spec_end ()
    106   };
    107 
    108   if (GNUNET_OK !=
    109       GNUNET_JSON_parse (response,
    110                          spec,
    111                          NULL, NULL))
    112   {
    113     GNUNET_break_op (0);
    114     return GNUNET_SYSERR;
    115   }
    116   fk->num_sign_keys = json_array_size (sk);
    117   fk->num_denom_keys = json_array_size (dk);
    118   fk->sign_keys = GNUNET_new_array (
    119     fk->num_sign_keys,
    120     struct TALER_EXCHANGE_FutureSigningPublicKey);
    121   fk->denom_keys = GNUNET_new_array (
    122     fk->num_denom_keys,
    123     struct TALER_EXCHANGE_FutureDenomPublicKey);
    124   ok = true;
    125   for (unsigned int i = 0; i<fk->num_sign_keys; i++)
    126   {
    127     json_t *j = json_array_get (sk,
    128                                 i);
    129     struct TALER_EXCHANGE_FutureSigningPublicKey *sign_key
    130       = &fk->sign_keys[i];
    131     struct GNUNET_JSON_Specification ispec[] = {
    132       GNUNET_JSON_spec_fixed_auto ("key",
    133                                    &sign_key->key),
    134       GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig",
    135                                    &sign_key->signkey_secmod_sig),
    136       GNUNET_JSON_spec_timestamp ("stamp_start",
    137                                   &sign_key->valid_from),
    138       GNUNET_JSON_spec_timestamp ("stamp_expire",
    139                                   &sign_key->valid_until),
    140       GNUNET_JSON_spec_timestamp ("stamp_end",
    141                                   &sign_key->valid_legal),
    142       GNUNET_JSON_spec_end ()
    143     };
    144 
    145     if (GNUNET_OK !=
    146         GNUNET_JSON_parse (j,
    147                            ispec,
    148                            NULL, NULL))
    149     {
    150       GNUNET_break_op (0);
    151       ok = false;
    152       break;
    153     }
    154     {
    155       struct GNUNET_TIME_Relative duration
    156         = GNUNET_TIME_absolute_get_difference (sign_key->valid_from.abs_time,
    157                                                sign_key->valid_until.abs_time);
    158 
    159       if (GNUNET_OK !=
    160           TALER_exchange_secmod_eddsa_verify (
    161             &sign_key->key,
    162             sign_key->valid_from,
    163             duration,
    164             &fk->signkey_secmod_public_key,
    165             &sign_key->signkey_secmod_sig))
    166       {
    167         GNUNET_break_op (0);
    168         ok = false;
    169         break;
    170       }
    171     }
    172   }
    173   for (unsigned int i = 0; i<fk->num_denom_keys; i++)
    174   {
    175     json_t *j = json_array_get (dk,
    176                                 i);
    177     struct TALER_EXCHANGE_FutureDenomPublicKey *denom_key
    178       = &fk->denom_keys[i];
    179     const char *section_name;
    180     struct GNUNET_JSON_Specification ispec[] = {
    181       TALER_JSON_spec_amount_any ("value",
    182                                   &denom_key->value),
    183       GNUNET_JSON_spec_timestamp ("stamp_start",
    184                                   &denom_key->valid_from),
    185       GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
    186                                   &denom_key->withdraw_valid_until),
    187       GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
    188                                   &denom_key->expire_deposit),
    189       GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
    190                                   &denom_key->expire_legal),
    191       TALER_JSON_spec_denom_pub ("denom_pub",
    192                                  &denom_key->key),
    193       TALER_JSON_spec_amount_any ("fee_withdraw",
    194                                   &denom_key->fee_withdraw),
    195       TALER_JSON_spec_amount_any ("fee_deposit",
    196                                   &denom_key->fee_deposit),
    197       TALER_JSON_spec_amount_any ("fee_refresh",
    198                                   &denom_key->fee_refresh),
    199       TALER_JSON_spec_amount_any ("fee_refund",
    200                                   &denom_key->fee_refund),
    201       GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig",
    202                                    &denom_key->denom_secmod_sig),
    203       GNUNET_JSON_spec_string ("section_name",
    204                                &section_name),
    205       GNUNET_JSON_spec_end ()
    206     };
    207 
    208     if (GNUNET_OK !=
    209         GNUNET_JSON_parse (j,
    210                            ispec,
    211                            NULL, NULL))
    212     {
    213       GNUNET_break_op (0);
    214 #if DEBUG
    215       json_dumpf (j,
    216                   stderr,
    217                   JSON_INDENT (2));
    218 #endif
    219       ok = false;
    220       break;
    221     }
    222 
    223     {
    224       struct TALER_DenominationHashP h_denom_pub;
    225       struct GNUNET_TIME_Relative duration
    226         = GNUNET_TIME_absolute_get_difference (
    227             denom_key->valid_from.abs_time,
    228             denom_key->withdraw_valid_until.abs_time);
    229 
    230       TALER_denom_pub_hash (&denom_key->key,
    231                             &h_denom_pub);
    232       switch (denom_key->key.bsign_pub_key->cipher)
    233       {
    234       case GNUNET_CRYPTO_BSA_RSA:
    235         {
    236           struct TALER_RsaPubHashP h_rsa;
    237 
    238           TALER_rsa_pub_hash (
    239             denom_key->key.bsign_pub_key->details.rsa_public_key,
    240             &h_rsa);
    241           if (GNUNET_OK !=
    242               TALER_exchange_secmod_rsa_verify (&h_rsa,
    243                                                 section_name,
    244                                                 denom_key->valid_from,
    245                                                 duration,
    246                                                 &fk->denom_secmod_public_key,
    247                                                 &denom_key->denom_secmod_sig))
    248           {
    249             GNUNET_break_op (0);
    250             ok = false;
    251             break;
    252           }
    253         }
    254         break;
    255       case GNUNET_CRYPTO_BSA_CS:
    256         {
    257           struct TALER_CsPubHashP h_cs;
    258 
    259           TALER_cs_pub_hash (
    260             &denom_key->key.bsign_pub_key->details.cs_public_key,
    261             &h_cs);
    262           if (GNUNET_OK !=
    263               TALER_exchange_secmod_cs_verify (&h_cs,
    264                                                section_name,
    265                                                denom_key->valid_from,
    266                                                duration,
    267                                                &fk->denom_secmod_cs_public_key,
    268                                                &denom_key->denom_secmod_sig))
    269           {
    270             GNUNET_break_op (0);
    271             ok = false;
    272             break;
    273           }
    274         }
    275         break;
    276       default:
    277         GNUNET_break_op (0);
    278         ok = false;
    279         break;
    280       }
    281     }
    282     if (! ok)
    283       break;
    284   }
    285   if (ok)
    286   {
    287     gh->cb (gh->cb_cls,
    288             &gkr);
    289   }
    290   for (unsigned int i = 0; i<fk->num_denom_keys; i++)
    291     TALER_denom_pub_free (&fk->denom_keys[i].key);
    292   GNUNET_free (fk->sign_keys);
    293   GNUNET_free (fk->denom_keys);
    294   return (ok) ? GNUNET_OK : GNUNET_SYSERR;
    295 }
    296 
    297 
    298 /**
    299  * Function called when we're done processing the
    300  * HTTP GET /management/keys request.
    301  *
    302  * @param cls the `struct TALER_EXCHANGE_ManagementGetKeysHandle *`
    303  * @param response_code HTTP response code, 0 on error
    304  * @param response response body, NULL if not in JSON
    305  */
    306 static void
    307 handle_get_keys_finished (void *cls,
    308                           long response_code,
    309                           const void *response)
    310 {
    311   struct TALER_EXCHANGE_ManagementGetKeysHandle *gh = cls;
    312   const json_t *json = response;
    313   struct TALER_EXCHANGE_ManagementGetKeysResponse gkr = {
    314     .hr.http_status = (unsigned int) response_code,
    315     .hr.reply = json
    316   };
    317 
    318   gh->job = NULL;
    319   switch (response_code)
    320   {
    321   case MHD_HTTP_OK:
    322     if (GNUNET_OK ==
    323         handle_ok (gh,
    324                    response))
    325     {
    326       gh->cb = NULL;
    327     }
    328     else
    329     {
    330       response_code = 0;
    331     }
    332     break;
    333   case MHD_HTTP_NOT_FOUND:
    334     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    335                 "Server did not find handler at `%s'. Did you configure the correct exchange base URL?\n",
    336                 gh->url);
    337     if (NULL != json)
    338     {
    339       gkr.hr.ec = TALER_JSON_get_error_code (json);
    340       gkr.hr.hint = TALER_JSON_get_error_hint (json);
    341     }
    342     else
    343     {
    344       gkr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    345       gkr.hr.hint = TALER_ErrorCode_get_hint (gkr.hr.ec);
    346     }
    347     break;
    348   default:
    349     /* unexpected response code */
    350     if (NULL != json)
    351     {
    352       gkr.hr.ec = TALER_JSON_get_error_code (json);
    353       gkr.hr.hint = TALER_JSON_get_error_hint (json);
    354     }
    355     else
    356     {
    357       gkr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    358       gkr.hr.hint = TALER_ErrorCode_get_hint (gkr.hr.ec);
    359     }
    360     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    361                 "Unexpected response code %u/%d for exchange management get keys\n",
    362                 (unsigned int) response_code,
    363                 (int) gkr.hr.ec);
    364     break;
    365   }
    366   if (NULL != gh->cb)
    367   {
    368     gh->cb (gh->cb_cls,
    369             &gkr);
    370     gh->cb = NULL;
    371   }
    372   TALER_EXCHANGE_get_management_keys_cancel (gh);
    373 };
    374 
    375 
    376 struct TALER_EXCHANGE_ManagementGetKeysHandle *
    377 TALER_EXCHANGE_get_management_keys (struct GNUNET_CURL_Context *ctx,
    378                                     const char *url,
    379                                     TALER_EXCHANGE_ManagementGetKeysCallback cb,
    380                                     void *cb_cls)
    381 {
    382   struct TALER_EXCHANGE_ManagementGetKeysHandle *gh;
    383   CURL *eh;
    384 
    385   gh = GNUNET_new (struct TALER_EXCHANGE_ManagementGetKeysHandle);
    386   gh->cb = cb;
    387   gh->cb_cls = cb_cls;
    388   gh->ctx = ctx;
    389   gh->url = TALER_url_join (url,
    390                             "management/keys",
    391                             NULL);
    392   if (NULL == gh->url)
    393   {
    394     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    395                 "Could not construct request URL.\n");
    396     GNUNET_free (gh);
    397     return NULL;
    398   }
    399   eh = TALER_EXCHANGE_curl_easy_get_ (gh->url);
    400   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    401               "Requesting URL '%s'\n",
    402               gh->url);
    403   gh->job = GNUNET_CURL_job_add (ctx,
    404                                  eh,
    405                                  &handle_get_keys_finished,
    406                                  gh);
    407   if (NULL == gh->job)
    408   {
    409     TALER_EXCHANGE_get_management_keys_cancel (gh);
    410     return NULL;
    411   }
    412   return gh;
    413 }
    414 
    415 
    416 void
    417 TALER_EXCHANGE_get_management_keys_cancel (
    418   struct TALER_EXCHANGE_ManagementGetKeysHandle *gh)
    419 {
    420   if (NULL != gh->job)
    421   {
    422     GNUNET_CURL_job_cancel (gh->job);
    423     gh->job = NULL;
    424   }
    425   GNUNET_free (gh->url);
    426   GNUNET_free (gh);
    427 }