LCOV - code coverage report
Current view: top level - lib - exchange_api_management_get_keys.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 70.2 % 161 113
Test Date: 2025-12-22 22:38:17 Functions: 100.0 % 4 4

            Line data    Source code
       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           19 : handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
      81              :            const json_t *response)
      82              : {
      83           19 :   struct TALER_EXCHANGE_ManagementGetKeysResponse gkr = {
      84              :     .hr.http_status = MHD_HTTP_OK,
      85              :     .hr.reply = response,
      86              :   };
      87           19 :   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           19 :     GNUNET_JSON_spec_array_const ("future_denoms",
      94              :                                   &dk),
      95           19 :     GNUNET_JSON_spec_array_const ("future_signkeys",
      96              :                                   &sk),
      97           19 :     GNUNET_JSON_spec_fixed_auto ("master_pub",
      98              :                                  &fk->master_pub),
      99           19 :     GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
     100              :                                  &fk->denom_secmod_public_key),
     101           19 :     GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
     102              :                                  &fk->denom_secmod_cs_public_key),
     103           19 :     GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
     104              :                                  &fk->signkey_secmod_public_key),
     105           19 :     GNUNET_JSON_spec_end ()
     106              :   };
     107              : 
     108           19 :   if (GNUNET_OK !=
     109           19 :       GNUNET_JSON_parse (response,
     110              :                          spec,
     111              :                          NULL, NULL))
     112              :   {
     113            0 :     GNUNET_break_op (0);
     114            0 :     return GNUNET_SYSERR;
     115              :   }
     116           19 :   fk->num_sign_keys = json_array_size (sk);
     117           19 :   fk->num_denom_keys = json_array_size (dk);
     118           19 :   fk->sign_keys = GNUNET_new_array (
     119              :     fk->num_sign_keys,
     120              :     struct TALER_EXCHANGE_FutureSigningPublicKey);
     121           19 :   fk->denom_keys = GNUNET_new_array (
     122              :     fk->num_denom_keys,
     123              :     struct TALER_EXCHANGE_FutureDenomPublicKey);
     124           19 :   ok = true;
     125           76 :   for (unsigned int i = 0; i<fk->num_sign_keys; i++)
     126              :   {
     127           57 :     json_t *j = json_array_get (sk,
     128              :                                 i);
     129           57 :     struct TALER_EXCHANGE_FutureSigningPublicKey *sign_key
     130           57 :       = &fk->sign_keys[i];
     131              :     struct GNUNET_JSON_Specification ispec[] = {
     132           57 :       GNUNET_JSON_spec_fixed_auto ("key",
     133              :                                    &sign_key->key),
     134           57 :       GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig",
     135              :                                    &sign_key->signkey_secmod_sig),
     136           57 :       GNUNET_JSON_spec_timestamp ("stamp_start",
     137              :                                   &sign_key->valid_from),
     138           57 :       GNUNET_JSON_spec_timestamp ("stamp_expire",
     139              :                                   &sign_key->valid_until),
     140           57 :       GNUNET_JSON_spec_timestamp ("stamp_end",
     141              :                                   &sign_key->valid_legal),
     142           57 :       GNUNET_JSON_spec_end ()
     143              :     };
     144              : 
     145           57 :     if (GNUNET_OK !=
     146           57 :         GNUNET_JSON_parse (j,
     147              :                            ispec,
     148              :                            NULL, NULL))
     149              :     {
     150            0 :       GNUNET_break_op (0);
     151            0 :       ok = false;
     152            0 :       break;
     153              :     }
     154              :     {
     155              :       struct GNUNET_TIME_Relative duration
     156           57 :         = GNUNET_TIME_absolute_get_difference (sign_key->valid_from.abs_time,
     157              :                                                sign_key->valid_until.abs_time);
     158              : 
     159           57 :       if (GNUNET_OK !=
     160           57 :           TALER_exchange_secmod_eddsa_verify (
     161           57 :             &sign_key->key,
     162              :             sign_key->valid_from,
     163              :             duration,
     164           57 :             &fk->signkey_secmod_public_key,
     165           57 :             &sign_key->signkey_secmod_sig))
     166              :       {
     167            0 :         GNUNET_break_op (0);
     168            0 :         ok = false;
     169            0 :         break;
     170              :       }
     171              :     }
     172              :   }
     173         5392 :   for (unsigned int i = 0; i<fk->num_denom_keys; i++)
     174              :   {
     175         5373 :     json_t *j = json_array_get (dk,
     176              :                                 i);
     177         5373 :     struct TALER_EXCHANGE_FutureDenomPublicKey *denom_key
     178         5373 :       = &fk->denom_keys[i];
     179              :     const char *section_name;
     180              :     struct GNUNET_JSON_Specification ispec[] = {
     181         5373 :       TALER_JSON_spec_amount_any ("value",
     182              :                                   &denom_key->value),
     183         5373 :       GNUNET_JSON_spec_timestamp ("stamp_start",
     184              :                                   &denom_key->valid_from),
     185         5373 :       GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
     186              :                                   &denom_key->withdraw_valid_until),
     187         5373 :       GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
     188              :                                   &denom_key->expire_deposit),
     189         5373 :       GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
     190              :                                   &denom_key->expire_legal),
     191         5373 :       TALER_JSON_spec_denom_pub ("denom_pub",
     192              :                                  &denom_key->key),
     193         5373 :       TALER_JSON_spec_amount_any ("fee_withdraw",
     194              :                                   &denom_key->fee_withdraw),
     195         5373 :       TALER_JSON_spec_amount_any ("fee_deposit",
     196              :                                   &denom_key->fee_deposit),
     197         5373 :       TALER_JSON_spec_amount_any ("fee_refresh",
     198              :                                   &denom_key->fee_refresh),
     199         5373 :       TALER_JSON_spec_amount_any ("fee_refund",
     200              :                                   &denom_key->fee_refund),
     201         5373 :       GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig",
     202              :                                    &denom_key->denom_secmod_sig),
     203         5373 :       GNUNET_JSON_spec_string ("section_name",
     204              :                                &section_name),
     205         5373 :       GNUNET_JSON_spec_end ()
     206              :     };
     207              : 
     208         5373 :     if (GNUNET_OK !=
     209         5373 :         GNUNET_JSON_parse (j,
     210              :                            ispec,
     211              :                            NULL, NULL))
     212              :     {
     213            0 :       GNUNET_break_op (0);
     214              : #if DEBUG
     215              :       json_dumpf (j,
     216              :                   stderr,
     217              :                   JSON_INDENT (2));
     218              : #endif
     219            0 :       ok = false;
     220            0 :       break;
     221              :     }
     222              : 
     223              :     {
     224              :       struct TALER_DenominationHashP h_denom_pub;
     225              :       struct GNUNET_TIME_Relative duration
     226         5373 :         = GNUNET_TIME_absolute_get_difference (
     227              :             denom_key->valid_from.abs_time,
     228              :             denom_key->withdraw_valid_until.abs_time);
     229              : 
     230         5373 :       TALER_denom_pub_hash (&denom_key->key,
     231              :                             &h_denom_pub);
     232         5373 :       switch (denom_key->key.bsign_pub_key->cipher)
     233              :       {
     234         2710 :       case GNUNET_CRYPTO_BSA_RSA:
     235              :         {
     236              :           struct TALER_RsaPubHashP h_rsa;
     237              : 
     238         2710 :           TALER_rsa_pub_hash (
     239         2710 :             denom_key->key.bsign_pub_key->details.rsa_public_key,
     240              :             &h_rsa);
     241         2710 :           if (GNUNET_OK !=
     242         2710 :               TALER_exchange_secmod_rsa_verify (&h_rsa,
     243              :                                                 section_name,
     244              :                                                 denom_key->valid_from,
     245              :                                                 duration,
     246         2710 :                                                 &fk->denom_secmod_public_key,
     247         2710 :                                                 &denom_key->denom_secmod_sig))
     248              :           {
     249            0 :             GNUNET_break_op (0);
     250            0 :             ok = false;
     251            0 :             break;
     252              :           }
     253              :         }
     254         2710 :         break;
     255         2663 :       case GNUNET_CRYPTO_BSA_CS:
     256              :         {
     257              :           struct TALER_CsPubHashP h_cs;
     258              : 
     259         2663 :           TALER_cs_pub_hash (
     260         2663 :             &denom_key->key.bsign_pub_key->details.cs_public_key,
     261              :             &h_cs);
     262         2663 :           if (GNUNET_OK !=
     263         2663 :               TALER_exchange_secmod_cs_verify (&h_cs,
     264              :                                                section_name,
     265              :                                                denom_key->valid_from,
     266              :                                                duration,
     267         2663 :                                                &fk->denom_secmod_cs_public_key,
     268         2663 :                                                &denom_key->denom_secmod_sig))
     269              :           {
     270            0 :             GNUNET_break_op (0);
     271            0 :             ok = false;
     272            0 :             break;
     273              :           }
     274              :         }
     275         2663 :         break;
     276            0 :       default:
     277            0 :         GNUNET_break_op (0);
     278            0 :         ok = false;
     279            0 :         break;
     280              :       }
     281              :     }
     282         5373 :     if (! ok)
     283            0 :       break;
     284              :   }
     285           19 :   if (ok)
     286              :   {
     287           19 :     gh->cb (gh->cb_cls,
     288              :             &gkr);
     289              :   }
     290         5392 :   for (unsigned int i = 0; i<fk->num_denom_keys; i++)
     291         5373 :     TALER_denom_pub_free (&fk->denom_keys[i].key);
     292           19 :   GNUNET_free (fk->sign_keys);
     293           19 :   GNUNET_free (fk->denom_keys);
     294           19 :   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           19 : handle_get_keys_finished (void *cls,
     308              :                           long response_code,
     309              :                           const void *response)
     310              : {
     311           19 :   struct TALER_EXCHANGE_ManagementGetKeysHandle *gh = cls;
     312           19 :   const json_t *json = response;
     313           19 :   struct TALER_EXCHANGE_ManagementGetKeysResponse gkr = {
     314           19 :     .hr.http_status = (unsigned int) response_code,
     315              :     .hr.reply = json
     316              :   };
     317              : 
     318           19 :   gh->job = NULL;
     319           19 :   switch (response_code)
     320              :   {
     321           19 :   case MHD_HTTP_OK:
     322           19 :     if (GNUNET_OK ==
     323           19 :         handle_ok (gh,
     324              :                    response))
     325              :     {
     326           19 :       gh->cb = NULL;
     327              :     }
     328              :     else
     329              :     {
     330            0 :       response_code = 0;
     331              :     }
     332           19 :     break;
     333            0 :   case MHD_HTTP_NOT_FOUND:
     334            0 :     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            0 :     if (NULL != json)
     338              :     {
     339            0 :       gkr.hr.ec = TALER_JSON_get_error_code (json);
     340            0 :       gkr.hr.hint = TALER_JSON_get_error_hint (json);
     341              :     }
     342              :     else
     343              :     {
     344            0 :       gkr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     345            0 :       gkr.hr.hint = TALER_ErrorCode_get_hint (gkr.hr.ec);
     346              :     }
     347            0 :     break;
     348            0 :   default:
     349              :     /* unexpected response code */
     350            0 :     if (NULL != json)
     351              :     {
     352            0 :       gkr.hr.ec = TALER_JSON_get_error_code (json);
     353            0 :       gkr.hr.hint = TALER_JSON_get_error_hint (json);
     354              :     }
     355              :     else
     356              :     {
     357            0 :       gkr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     358            0 :       gkr.hr.hint = TALER_ErrorCode_get_hint (gkr.hr.ec);
     359              :     }
     360            0 :     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            0 :     break;
     365              :   }
     366           19 :   if (NULL != gh->cb)
     367              :   {
     368            0 :     gh->cb (gh->cb_cls,
     369              :             &gkr);
     370            0 :     gh->cb = NULL;
     371              :   }
     372           19 :   TALER_EXCHANGE_get_management_keys_cancel (gh);
     373           19 : };
     374              : 
     375              : 
     376              : struct TALER_EXCHANGE_ManagementGetKeysHandle *
     377           19 : 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           19 :   gh = GNUNET_new (struct TALER_EXCHANGE_ManagementGetKeysHandle);
     386           19 :   gh->cb = cb;
     387           19 :   gh->cb_cls = cb_cls;
     388           19 :   gh->ctx = ctx;
     389           19 :   gh->url = TALER_url_join (url,
     390              :                             "management/keys",
     391              :                             NULL);
     392           19 :   if (NULL == gh->url)
     393              :   {
     394            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     395              :                 "Could not construct request URL.\n");
     396            0 :     GNUNET_free (gh);
     397            0 :     return NULL;
     398              :   }
     399           19 :   eh = TALER_EXCHANGE_curl_easy_get_ (gh->url);
     400           19 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     401              :               "Requesting URL '%s'\n",
     402              :               gh->url);
     403           19 :   gh->job = GNUNET_CURL_job_add (ctx,
     404              :                                  eh,
     405              :                                  &handle_get_keys_finished,
     406              :                                  gh);
     407           19 :   if (NULL == gh->job)
     408              :   {
     409            0 :     TALER_EXCHANGE_get_management_keys_cancel (gh);
     410            0 :     return NULL;
     411              :   }
     412           19 :   return gh;
     413              : }
     414              : 
     415              : 
     416              : void
     417           19 : TALER_EXCHANGE_get_management_keys_cancel (
     418              :   struct TALER_EXCHANGE_ManagementGetKeysHandle *gh)
     419              : {
     420           19 :   if (NULL != gh->job)
     421              :   {
     422            0 :     GNUNET_CURL_job_cancel (gh->job);
     423            0 :     gh->job = NULL;
     424              :   }
     425           19 :   GNUNET_free (gh->url);
     426           19 :   GNUNET_free (gh);
     427           19 : }
        

Generated by: LCOV version 2.0-1