exchange

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

auditor_api_put_deposit_confirmation.c (14647B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-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/auditor_api_put_deposit_confirmation.c
     19  * @brief Implementation of the /deposit request of the auditor's HTTP API
     20  * @author Christian Grothoff
     21  */
     22 #include <jansson.h>
     23 #include <microhttpd.h> /* just for HTTP status codes */
     24 #include <gnunet/gnunet_util_lib.h>
     25 #include <gnunet/gnunet_json_lib.h>
     26 #include <gnunet/gnunet_curl_lib.h>
     27 #include "taler/taler_util.h"
     28 #include "taler/taler_curl_lib.h"
     29 #include "taler/taler_json_lib.h"
     30 #include "taler/taler_auditor_service.h"
     31 #include "auditor_api_curl_defaults.h"
     32 
     33 
     34 /**
     35  * @brief A DepositConfirmation Handle
     36  */
     37 struct TALER_AUDITOR_DepositConfirmationHandle
     38 {
     39 
     40   /**
     41    * The url for this request.
     42    */
     43   char *url;
     44 
     45   /**
     46    * Context for #TEH_curl_easy_post(). Keeps the data that must
     47    * persist for Curl to make the upload.
     48    */
     49   struct TALER_CURL_PostContext ctx;
     50 
     51   /**
     52    * Handle for the request.
     53    */
     54   struct GNUNET_CURL_Job *job;
     55 
     56   /**
     57    * Function to call with the result.
     58    */
     59   TALER_AUDITOR_DepositConfirmationResultCallback cb;
     60 
     61   /**
     62    * Closure for @a cb.
     63    */
     64   void *cb_cls;
     65 
     66 };
     67 
     68 
     69 /**
     70  * Function called when we're done processing the
     71  * HTTP /deposit-confirmation request.
     72  *
     73  * @param cls the `struct TALER_AUDITOR_DepositConfirmationHandle`
     74  * @param response_code HTTP response code, 0 on error
     75  * @param djson parsed JSON result, NULL on error
     76  */
     77 static void
     78 handle_deposit_confirmation_finished (void *cls,
     79                                       long response_code,
     80                                       const void *djson)
     81 {
     82   const json_t *json = djson;
     83   struct TALER_AUDITOR_DepositConfirmationHandle *dh = cls;
     84   struct TALER_AUDITOR_DepositConfirmationResponse dcr = {
     85     .hr.reply = json,
     86     .hr.http_status = (unsigned int) response_code
     87   };
     88 
     89   dh->job = NULL;
     90   switch (response_code)
     91   {
     92   case 0:
     93     dcr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     94     break;
     95   case MHD_HTTP_OK:
     96     dcr.hr.ec = TALER_EC_NONE;
     97     break;
     98   case MHD_HTTP_BAD_REQUEST:
     99     dcr.hr.ec = TALER_JSON_get_error_code (json);
    100     dcr.hr.hint = TALER_JSON_get_error_hint (json);
    101     /* This should never happen, either us or the auditor is buggy
    102        (or API version conflict); just pass JSON reply to the application */
    103     break;
    104   case MHD_HTTP_FORBIDDEN:
    105     dcr.hr.ec = TALER_JSON_get_error_code (json);
    106     dcr.hr.hint = TALER_JSON_get_error_hint (json);
    107     /* Nothing really to verify, auditor says one of the signatures is
    108        invalid; as we checked them, this should never happen, we
    109        should pass the JSON reply to the application */
    110     break;
    111   case MHD_HTTP_NOT_FOUND:
    112     dcr.hr.ec = TALER_JSON_get_error_code (json);
    113     dcr.hr.hint = TALER_JSON_get_error_hint (json);
    114     /* Nothing really to verify, this should never
    115        happen, we should pass the JSON reply to the application */
    116     break;
    117   case MHD_HTTP_GONE:
    118     dcr.hr.ec = TALER_JSON_get_error_code (json);
    119     dcr.hr.hint = TALER_JSON_get_error_hint (json);
    120     /* Nothing really to verify, auditor says one of the signatures is
    121        invalid; as we checked them, this should never happen, we
    122        should pass the JSON reply to the application */
    123     break;
    124   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    125     dcr.hr.ec = TALER_JSON_get_error_code (json);
    126     dcr.hr.hint = TALER_JSON_get_error_hint (json);
    127     /* Server had an internal issue; we should retry, but this API
    128        leaves this to the application */
    129     break;
    130   default:
    131     /* unexpected response code */
    132     dcr.hr.ec = TALER_JSON_get_error_code (json);
    133     dcr.hr.hint = TALER_JSON_get_error_hint (json);
    134     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    135                 "Unexpected response code %u/%d for auditor deposit confirmation\n",
    136                 (unsigned int) response_code,
    137                 dcr.hr.ec);
    138     break;
    139   }
    140   dh->cb (dh->cb_cls,
    141           &dcr);
    142   TALER_AUDITOR_deposit_confirmation_cancel (dh);
    143 }
    144 
    145 
    146 /**
    147  * Verify signature information about the deposit-confirmation.
    148  *
    149  * @param h_wire hash of merchant wire details
    150  * @param h_policy hash over the policy extension, if any
    151  * @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the auditor)
    152  * @param exchange_timestamp timestamp when the deposit was received by the wallet
    153  * @param wire_deadline by what time must the amount be wired to the merchant
    154  * @param refund_deadline date until which the merchant can issue a refund to the customer via the auditor (can be zero if refunds are not allowed); must not be after the @a wire_deadline
    155  * @param amount_without_fee the amount confirmed to be wired by the exchange to the merchant
    156  * @param num_coins number of coins involved
    157  * @param coin_sigs array of @a num_coins coin signatures
    158  * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
    159  * @param exchange_sig the signature made with purpose #TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT
    160  * @param exchange_pub the public key of the exchange that matches @a exchange_sig
    161  * @param master_pub master public key of the exchange
    162  * @param ep_start when does @a exchange_pub validity start
    163  * @param ep_expire when does @a exchange_pub usage end
    164  * @param ep_end when does @a exchange_pub legal validity end
    165  * @param master_sig master signature affirming validity of @a exchange_pub
    166  * @return #GNUNET_OK if signatures are OK, #GNUNET_SYSERR if not
    167  */
    168 static enum GNUNET_GenericReturnValue
    169 verify_signatures (
    170   const struct TALER_MerchantWireHashP *h_wire,
    171   const struct TALER_ExtensionPolicyHashP *h_policy,
    172   const struct TALER_PrivateContractHashP *h_contract_terms,
    173   struct GNUNET_TIME_Timestamp exchange_timestamp,
    174   struct GNUNET_TIME_Timestamp wire_deadline,
    175   struct GNUNET_TIME_Timestamp refund_deadline,
    176   const struct TALER_Amount *amount_without_fee,
    177   unsigned int num_coins,
    178   const struct TALER_CoinSpendSignatureP *coin_sigs[
    179     static num_coins],
    180   const struct TALER_MerchantPublicKeyP *merchant_pub,
    181   const struct TALER_ExchangePublicKeyP *exchange_pub,
    182   const struct TALER_ExchangeSignatureP *exchange_sig,
    183   const struct TALER_MasterPublicKeyP *master_pub,
    184   struct GNUNET_TIME_Timestamp ep_start,
    185   struct GNUNET_TIME_Timestamp ep_expire,
    186   struct GNUNET_TIME_Timestamp ep_end,
    187   const struct TALER_MasterSignatureP *master_sig)
    188 {
    189   if (GNUNET_OK !=
    190       TALER_exchange_online_deposit_confirmation_verify (
    191         h_contract_terms,
    192         h_wire,
    193         h_policy,
    194         exchange_timestamp,
    195         wire_deadline,
    196         refund_deadline,
    197         amount_without_fee,
    198         num_coins,
    199         coin_sigs,
    200         merchant_pub,
    201         exchange_pub,
    202         exchange_sig))
    203   {
    204     GNUNET_break_op (0);
    205     TALER_LOG_WARNING (
    206       "Invalid signature on /deposit-confirmation request!\n");
    207     {
    208       TALER_LOG_DEBUG ("... amount_without_fee was %s\n",
    209                        TALER_amount2s (amount_without_fee));
    210     }
    211     return GNUNET_SYSERR;
    212   }
    213 
    214   if (GNUNET_OK !=
    215       TALER_exchange_offline_signkey_validity_verify (
    216         exchange_pub,
    217         ep_start,
    218         ep_expire,
    219         ep_end,
    220         master_pub,
    221         master_sig))
    222   {
    223     GNUNET_break (0);
    224     TALER_LOG_WARNING ("Invalid signature on exchange signing key!\n");
    225     return GNUNET_SYSERR;
    226   }
    227   if (GNUNET_TIME_absolute_is_past (ep_end.abs_time))
    228   {
    229     GNUNET_break (0);
    230     TALER_LOG_WARNING ("Exchange signing key is no longer valid!\n");
    231     return GNUNET_SYSERR;
    232   }
    233   return GNUNET_OK;
    234 }
    235 
    236 
    237 struct TALER_AUDITOR_DepositConfirmationHandle *
    238 TALER_AUDITOR_deposit_confirmation (
    239   struct GNUNET_CURL_Context *ctx,
    240   const char *url,
    241   const struct TALER_MerchantWireHashP *h_wire,
    242   const struct TALER_ExtensionPolicyHashP *h_policy,
    243   const struct TALER_PrivateContractHashP *h_contract_terms,
    244   struct GNUNET_TIME_Timestamp exchange_timestamp,
    245   struct GNUNET_TIME_Timestamp wire_deadline,
    246   struct GNUNET_TIME_Timestamp refund_deadline,
    247   const struct TALER_Amount *total_without_fee,
    248   unsigned int num_coins,
    249   const struct TALER_CoinSpendPublicKeyP *coin_pubs[
    250     static num_coins],
    251   const struct TALER_CoinSpendSignatureP *coin_sigs[
    252     static num_coins],
    253   const struct TALER_MerchantPublicKeyP *merchant_pub,
    254   const struct TALER_ExchangePublicKeyP *exchange_pub,
    255   const struct TALER_ExchangeSignatureP *exchange_sig,
    256   const struct TALER_MasterPublicKeyP *master_pub,
    257   struct GNUNET_TIME_Timestamp ep_start,
    258   struct GNUNET_TIME_Timestamp ep_expire,
    259   struct GNUNET_TIME_Timestamp ep_end,
    260   const struct TALER_MasterSignatureP *master_sig,
    261   TALER_AUDITOR_DepositConfirmationResultCallback cb,
    262   void *cb_cls)
    263 {
    264   struct TALER_AUDITOR_DepositConfirmationHandle *dh;
    265   json_t *deposit_confirmation_obj;
    266   CURL *eh;
    267   json_t *jcoin_sigs;
    268   json_t *jcoin_pubs;
    269 
    270   if (0 == num_coins)
    271   {
    272     GNUNET_break (0);
    273     return NULL;
    274   }
    275   if (GNUNET_OK !=
    276       verify_signatures (h_wire,
    277                          h_policy,
    278                          h_contract_terms,
    279                          exchange_timestamp,
    280                          wire_deadline,
    281                          refund_deadline,
    282                          total_without_fee,
    283                          num_coins,
    284                          coin_sigs,
    285                          merchant_pub,
    286                          exchange_pub,
    287                          exchange_sig,
    288                          master_pub,
    289                          ep_start,
    290                          ep_expire,
    291                          ep_end,
    292                          master_sig))
    293   {
    294     GNUNET_break_op (0);
    295     return NULL;
    296   }
    297   jcoin_sigs = json_array ();
    298   GNUNET_assert (NULL != jcoin_sigs);
    299   jcoin_pubs = json_array ();
    300   GNUNET_assert (NULL != jcoin_pubs);
    301   for (unsigned int i = 0; i<num_coins; i++)
    302   {
    303     GNUNET_assert (0 ==
    304                    json_array_append_new (jcoin_sigs,
    305                                           GNUNET_JSON_from_data_auto (
    306                                             coin_sigs[i])));
    307     GNUNET_assert (0 ==
    308                    json_array_append_new (jcoin_pubs,
    309                                           GNUNET_JSON_from_data_auto (
    310                                             coin_pubs[i])));
    311   }
    312   deposit_confirmation_obj
    313     = GNUNET_JSON_PACK (
    314         GNUNET_JSON_pack_data_auto ("h_wire",
    315                                     h_wire),
    316         GNUNET_JSON_pack_data_auto ("h_policy",
    317                                     h_policy),
    318         GNUNET_JSON_pack_data_auto ("h_contract_terms",
    319                                     h_contract_terms),
    320         GNUNET_JSON_pack_timestamp ("exchange_timestamp",
    321                                     exchange_timestamp),
    322         GNUNET_JSON_pack_allow_null (
    323           GNUNET_JSON_pack_timestamp ("refund_deadline",
    324                                       refund_deadline)),
    325         GNUNET_JSON_pack_timestamp ("wire_deadline",
    326                                     wire_deadline),
    327         TALER_JSON_pack_amount ("total_without_fee",
    328                                 total_without_fee),
    329         GNUNET_JSON_pack_array_steal ("coin_pubs",
    330                                       jcoin_pubs),
    331         GNUNET_JSON_pack_array_steal ("coin_sigs",
    332                                       jcoin_sigs),
    333         GNUNET_JSON_pack_data_auto ("merchant_pub",
    334                                     merchant_pub),
    335         GNUNET_JSON_pack_data_auto ("exchange_sig",
    336                                     exchange_sig),
    337         GNUNET_JSON_pack_data_auto ("master_pub",
    338                                     master_pub),
    339         GNUNET_JSON_pack_timestamp ("ep_start",
    340                                     ep_start),
    341         GNUNET_JSON_pack_timestamp ("ep_expire",
    342                                     ep_expire),
    343         GNUNET_JSON_pack_timestamp ("ep_end",
    344                                     ep_end),
    345         GNUNET_JSON_pack_data_auto ("master_sig",
    346                                     master_sig),
    347         GNUNET_JSON_pack_data_auto ("exchange_pub",
    348                                     exchange_pub));
    349   dh = GNUNET_new (struct TALER_AUDITOR_DepositConfirmationHandle);
    350   dh->cb = cb;
    351   dh->cb_cls = cb_cls;
    352   dh->url = TALER_url_join (url,
    353                             "deposit-confirmation",
    354                             NULL);
    355   if (NULL == dh->url)
    356   {
    357     GNUNET_free (dh);
    358     return NULL;
    359   }
    360   eh = TALER_AUDITOR_curl_easy_get_ (dh->url);
    361   if ( (NULL == eh) ||
    362        (CURLE_OK !=
    363         curl_easy_setopt (eh,
    364                           CURLOPT_CUSTOMREQUEST,
    365                           "PUT")) ||
    366        (GNUNET_OK !=
    367         TALER_curl_easy_post (&dh->ctx,
    368                               eh,
    369                               deposit_confirmation_obj)) )
    370   {
    371     GNUNET_break (0);
    372     if (NULL != eh)
    373       curl_easy_cleanup (eh);
    374     json_decref (deposit_confirmation_obj);
    375     GNUNET_free (dh->url);
    376     GNUNET_free (dh);
    377     return NULL;
    378   }
    379   json_decref (deposit_confirmation_obj);
    380   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    381               "URL for deposit-confirmation: `%s'\n",
    382               dh->url);
    383   dh->job = GNUNET_CURL_job_add2 (ctx,
    384                                   eh,
    385                                   dh->ctx.headers,
    386                                   &handle_deposit_confirmation_finished,
    387                                   dh);
    388   {
    389     /* Disable 100 continue processing */
    390     struct curl_slist *x_headers;
    391 
    392     x_headers = curl_slist_append (NULL,
    393                                    "Expect:");
    394     GNUNET_CURL_extend_headers (dh->job,
    395                                 x_headers);
    396     curl_slist_free_all (x_headers);
    397   }
    398   return dh;
    399 }
    400 
    401 
    402 void
    403 TALER_AUDITOR_deposit_confirmation_cancel (
    404   struct TALER_AUDITOR_DepositConfirmationHandle *deposit_confirmation)
    405 {
    406   if (NULL != deposit_confirmation->job)
    407   {
    408     GNUNET_CURL_job_cancel (deposit_confirmation->job);
    409     deposit_confirmation->job = NULL;
    410   }
    411   GNUNET_free (deposit_confirmation->url);
    412   TALER_curl_easy_post_finished (&deposit_confirmation->ctx);
    413   GNUNET_free (deposit_confirmation);
    414 }
    415 
    416 
    417 /* end of auditor_api_put_deposit_confirmation.c */