exchange

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

auditor_api_deposit_confirmation.c (14703B)


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