LCOV - code coverage report
Current view: top level - lib - auditor_api_deposit_confirmation.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 58 111 52.3 %
Date: 2025-06-05 21:03:14 Functions: 4 4 100.0 %

          Line data    Source code
       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 "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_util.h"
      29             : #include "taler_curl_lib.h"
      30             : #include "taler_json_lib.h"
      31             : #include "taler_auditor_service.h"
      32             : #include "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          10 : handle_deposit_confirmation_finished (void *cls,
      81             :                                       long response_code,
      82             :                                       const void *djson)
      83             : {
      84          10 :   const json_t *json = djson;
      85          10 :   struct TALER_AUDITOR_DepositConfirmationHandle *dh = cls;
      86          10 :   struct TALER_AUDITOR_DepositConfirmationResponse dcr = {
      87             :     .hr.reply = json,
      88          10 :     .hr.http_status = (unsigned int) response_code
      89             :   };
      90             : 
      91          10 :   dh->job = NULL;
      92          10 :   switch (response_code)
      93             :   {
      94           0 :   case 0:
      95           0 :     dcr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
      96           0 :     break;
      97          10 :   case MHD_HTTP_OK:
      98          10 :     dcr.hr.ec = TALER_EC_NONE;
      99          10 :     break;
     100           0 :   case MHD_HTTP_BAD_REQUEST:
     101           0 :     dcr.hr.ec = TALER_JSON_get_error_code (json);
     102           0 :     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           0 :     break;
     106           0 :   case MHD_HTTP_FORBIDDEN:
     107           0 :     dcr.hr.ec = TALER_JSON_get_error_code (json);
     108           0 :     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           0 :     break;
     113           0 :   case MHD_HTTP_NOT_FOUND:
     114           0 :     dcr.hr.ec = TALER_JSON_get_error_code (json);
     115           0 :     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           0 :     break;
     119           0 :   case MHD_HTTP_GONE:
     120           0 :     dcr.hr.ec = TALER_JSON_get_error_code (json);
     121           0 :     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           0 :     break;
     126           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     127           0 :     dcr.hr.ec = TALER_JSON_get_error_code (json);
     128           0 :     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           0 :     break;
     132           0 :   default:
     133             :     /* unexpected response code */
     134           0 :     dcr.hr.ec = TALER_JSON_get_error_code (json);
     135           0 :     dcr.hr.hint = TALER_JSON_get_error_hint (json);
     136           0 :     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           0 :     break;
     141             :   }
     142          10 :   dh->cb (dh->cb_cls,
     143             :           &dcr);
     144          10 :   TALER_AUDITOR_deposit_confirmation_cancel (dh);
     145          10 : }
     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          10 : 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          10 : {
     191          10 :   if (GNUNET_OK !=
     192          10 :       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           0 :     GNUNET_break_op (0);
     207           0 :     TALER_LOG_WARNING (
     208             :       "Invalid signature on /deposit-confirmation request!\n");
     209             :     {
     210           0 :       TALER_LOG_DEBUG ("... amount_without_fee was %s\n",
     211             :                        TALER_amount2s (amount_without_fee));
     212             :     }
     213           0 :     return GNUNET_SYSERR;
     214             :   }
     215             : 
     216          10 :   if (GNUNET_OK !=
     217          10 :       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           0 :     GNUNET_break (0);
     226           0 :     TALER_LOG_WARNING ("Invalid signature on exchange signing key!\n");
     227           0 :     return GNUNET_SYSERR;
     228             :   }
     229          10 :   if (GNUNET_TIME_absolute_is_past (ep_end.abs_time))
     230             :   {
     231           0 :     GNUNET_break (0);
     232           0 :     TALER_LOG_WARNING ("Exchange signing key is no longer valid!\n");
     233           0 :     return GNUNET_SYSERR;
     234             :   }
     235          10 :   return GNUNET_OK;
     236             : }
     237             : 
     238             : 
     239             : struct TALER_AUDITOR_DepositConfirmationHandle *
     240          10 : 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          10 : {
     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          10 :   if (0 == num_coins)
     273             :   {
     274           0 :     GNUNET_break (0);
     275           0 :     return NULL;
     276             :   }
     277          10 :   if (GNUNET_OK !=
     278          10 :       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           0 :     GNUNET_break_op (0);
     297           0 :     return NULL;
     298             :   }
     299          10 :   jcoin_sigs = json_array ();
     300          10 :   GNUNET_assert (NULL != jcoin_sigs);
     301          10 :   jcoin_pubs = json_array ();
     302          10 :   GNUNET_assert (NULL != jcoin_pubs);
     303          20 :   for (unsigned int i = 0; i<num_coins; i++)
     304             :   {
     305          10 :     GNUNET_assert (0 ==
     306             :                    json_array_append_new (jcoin_sigs,
     307             :                                           GNUNET_JSON_from_data_auto (
     308             :                                             coin_sigs[i])));
     309          10 :     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          10 :     = 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          10 :   dh = GNUNET_new (struct TALER_AUDITOR_DepositConfirmationHandle);
     352          10 :   dh->cb = cb;
     353          10 :   dh->cb_cls = cb_cls;
     354          10 :   dh->url = TALER_url_join (url,
     355             :                             "deposit-confirmation",
     356             :                             NULL);
     357          10 :   if (NULL == dh->url)
     358             :   {
     359           0 :     GNUNET_free (dh);
     360           0 :     return NULL;
     361             :   }
     362          10 :   eh = TALER_AUDITOR_curl_easy_get_ (dh->url);
     363          20 :   if ( (NULL == eh) ||
     364             :        (CURLE_OK !=
     365          10 :         curl_easy_setopt (eh,
     366             :                           CURLOPT_CUSTOMREQUEST,
     367          10 :                           "PUT")) ||
     368             :        (GNUNET_OK !=
     369          10 :         TALER_curl_easy_post (&dh->ctx,
     370             :                               eh,
     371             :                               deposit_confirmation_obj)) )
     372             :   {
     373           0 :     GNUNET_break (0);
     374           0 :     if (NULL != eh)
     375           0 :       curl_easy_cleanup (eh);
     376           0 :     json_decref (deposit_confirmation_obj);
     377           0 :     GNUNET_free (dh->url);
     378           0 :     GNUNET_free (dh);
     379           0 :     return NULL;
     380             :   }
     381          10 :   json_decref (deposit_confirmation_obj);
     382          10 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     383             :               "URL for deposit-confirmation: `%s'\n",
     384             :               dh->url);
     385          20 :   dh->job = GNUNET_CURL_job_add2 (ctx,
     386             :                                   eh,
     387          10 :                                   dh->ctx.headers,
     388             :                                   &handle_deposit_confirmation_finished,
     389             :                                   dh);
     390             :   {
     391             :     /* Disable 100 continue processing */
     392             :     struct curl_slist *x_headers;
     393             : 
     394          10 :     x_headers = curl_slist_append (NULL,
     395             :                                    "Expect:");
     396          10 :     GNUNET_CURL_extend_headers (dh->job,
     397             :                                 x_headers);
     398          10 :     curl_slist_free_all (x_headers);
     399             :   }
     400          10 :   return dh;
     401             : }
     402             : 
     403             : 
     404             : void
     405          10 : TALER_AUDITOR_deposit_confirmation_cancel (
     406             :   struct TALER_AUDITOR_DepositConfirmationHandle *deposit_confirmation)
     407             : {
     408          10 :   if (NULL != deposit_confirmation->job)
     409             :   {
     410           0 :     GNUNET_CURL_job_cancel (deposit_confirmation->job);
     411           0 :     deposit_confirmation->job = NULL;
     412             :   }
     413          10 :   GNUNET_free (deposit_confirmation->url);
     414          10 :   TALER_curl_easy_post_finished (&deposit_confirmation->ctx);
     415          10 :   GNUNET_free (deposit_confirmation);
     416          10 : }
     417             : 
     418             : 
     419             : /* end of auditor_api_deposit_confirmation.c */

Generated by: LCOV version 1.16