LCOV - code coverage report
Current view: top level - lib - exchange_api_blinding_prepare.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 84 147 57.1 %
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) 2025 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_blinding_prepare.c
      19             :  * @brief Implementation of /blinding-prepare requests
      20             :  * @author Özgür Kesim
      21             :  */
      22             : 
      23             : #include "platform.h"
      24             : #include <gnunet/gnunet_common.h>
      25             : #include <jansson.h>
      26             : #include <microhttpd.h> /* just for HTTP status codes */
      27             : #include <gnunet/gnunet_util_lib.h>
      28             : #include <gnunet/gnunet_json_lib.h>
      29             : #include <gnunet/gnunet_curl_lib.h>
      30             : #include <sys/wait.h>
      31             : #include "taler_curl_lib.h"
      32             : #include "taler_error_codes.h"
      33             : #include "taler_json_lib.h"
      34             : #include "taler_exchange_service.h"
      35             : #include "exchange_api_common.h"
      36             : #include "exchange_api_handle.h"
      37             : #include "taler_signatures.h"
      38             : #include "exchange_api_curl_defaults.h"
      39             : #include "taler_util.h"
      40             : 
      41             : /**
      42             :  * A /blinding-prepare request-handle
      43             :  */
      44             : struct TALER_EXCHANGE_BlindingPrepareHandle
      45             : {
      46             :   /**
      47             :    * number of elements to prepare
      48             :    */
      49             :   size_t num;
      50             : 
      51             :   /**
      52             :    * True, if this operation is for melting (or withdraw otherwise).
      53             :    */
      54             :   bool for_melt;
      55             : 
      56             :   /**
      57             :    * The seed for the batch of nonces.
      58             :    */
      59             :   const struct TALER_BlindingMasterSeedP *seed;
      60             : 
      61             :   /**
      62             :    * The url for this request.
      63             :    */
      64             :   char *url;
      65             : 
      66             :   /**
      67             :    * Context for curl.
      68             :    */
      69             :   struct GNUNET_CURL_Context *curl_ctx;
      70             : 
      71             :   /**
      72             :    * CURL handle for the request job.
      73             :    */
      74             :   struct GNUNET_CURL_Job *job;
      75             : 
      76             :   /**
      77             :    * Post Context
      78             :    */
      79             :   struct TALER_CURL_PostContext post_ctx;
      80             : 
      81             :   /**
      82             :    * Function to call with withdraw response results.
      83             :    */
      84             :   TALER_EXCHANGE_BlindingPrepareCallback callback;
      85             : 
      86             :   /**
      87             :    * Closure for @e callback
      88             :    */
      89             :   void *callback_cls;
      90             : 
      91             : };
      92             : 
      93             : 
      94             : /**
      95             :  * We got a 200 OK response for the /blinding-prepare operation.
      96             :  * Extract the r_pub values and return them to the caller via the callback
      97             :  *
      98             :  * @param handle operation handle
      99             :  * @param response response details
     100             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
     101             :  */
     102             : static enum GNUNET_GenericReturnValue
     103          50 : blinding_prepare_ok (struct TALER_EXCHANGE_BlindingPrepareHandle *handle,
     104             :                      struct TALER_EXCHANGE_BlindingPrepareResponse *response)
     105             : {
     106             :   const json_t *j_r_pubs;
     107             :   const char *cipher;
     108             :   struct GNUNET_JSON_Specification spec[] = {
     109          50 :     GNUNET_JSON_spec_string ("cipher",
     110             :                              &cipher),
     111          50 :     GNUNET_JSON_spec_array_const ("r_pubs",
     112             :                                   &j_r_pubs),
     113          50 :     GNUNET_JSON_spec_end ()
     114             :   };
     115             : 
     116          50 :   if (GNUNET_OK !=
     117          50 :       GNUNET_JSON_parse (response->hr.reply,
     118             :                          spec,
     119             :                          NULL, NULL))
     120             :   {
     121           0 :     GNUNET_break_op (0);
     122           0 :     return GNUNET_SYSERR;
     123             :   }
     124             : 
     125          50 :   if (strcmp ("CS", cipher))
     126             :   {
     127           0 :     GNUNET_break_op (0);
     128           0 :     return GNUNET_SYSERR;
     129             :   }
     130             : 
     131          50 :   if (json_array_size (j_r_pubs)
     132          50 :       != handle->num)
     133             :   {
     134           0 :     GNUNET_break_op (0);
     135           0 :     return GNUNET_SYSERR;
     136             :   }
     137             : 
     138          50 :   {
     139          50 :     size_t num = handle->num;
     140             :     const json_t *j_pair;
     141             :     size_t idx;
     142          50 :     struct TALER_ExchangeBlindingValues blinding_values[GNUNET_NZL (num)];
     143             : 
     144          50 :     memset (blinding_values,
     145             :             0,
     146             :             sizeof(blinding_values));
     147             : 
     148         148 :     json_array_foreach (j_r_pubs, idx, j_pair) {
     149             :       struct GNUNET_CRYPTO_BlindingInputValues *bi =
     150          98 :         GNUNET_new (struct GNUNET_CRYPTO_BlindingInputValues);
     151          98 :       struct GNUNET_CRYPTO_CSPublicRPairP *csv = &bi->details.cs_values;
     152             :       struct GNUNET_JSON_Specification tuple[] =  {
     153          98 :         GNUNET_JSON_spec_fixed (NULL,
     154          98 :                                 &csv->r_pub[0],
     155             :                                 sizeof(csv->r_pub[0])),
     156          98 :         GNUNET_JSON_spec_fixed (NULL,
     157          98 :                                 &csv->r_pub[1],
     158             :                                 sizeof(csv->r_pub[1])),
     159          98 :         GNUNET_JSON_spec_end ()
     160             :       };
     161             :       struct GNUNET_JSON_Specification jspec[] = {
     162          98 :         TALER_JSON_spec_tuple_of (NULL, tuple),
     163          98 :         GNUNET_JSON_spec_end ()
     164             :       };
     165             :       const char *err_msg;
     166             :       unsigned int err_line;
     167             : 
     168          98 :       if (GNUNET_OK !=
     169          98 :           GNUNET_JSON_parse (j_pair,
     170             :                              jspec,
     171             :                              &err_msg,
     172             :                              &err_line))
     173             :       {
     174           0 :         GNUNET_break_op (0);
     175           0 :         GNUNET_free (bi);
     176           0 :         for (size_t i=0; i < idx; i++)
     177           0 :           TALER_denom_ewv_free (&blinding_values[i]);
     178           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     179             :                     "Error while parsing response: in line %d: %s",
     180             :                     err_line,
     181             :                     err_msg);
     182           0 :         return GNUNET_SYSERR;
     183             :       }
     184             : 
     185          98 :       bi->cipher = GNUNET_CRYPTO_BSA_CS;
     186          98 :       bi->rc = 1;
     187          98 :       blinding_values[idx].blinding_inputs = bi;
     188             :     }
     189             : 
     190          50 :     response->details.ok.blinding_values = blinding_values;
     191          50 :     response->details.ok.num_blinding_values = num;
     192             : 
     193          50 :     handle->callback (
     194             :       handle->callback_cls,
     195             :       response);
     196             : 
     197         148 :     for (size_t i = 0; i < num; i++)
     198          98 :       TALER_denom_ewv_free (&blinding_values[i]);
     199             :   }
     200          50 :   return GNUNET_OK;
     201             : }
     202             : 
     203             : 
     204             : /**
     205             :  * Function called when we're done processing the HTTP /blinding-prepare request.
     206             :  *
     207             :  * @param cls the `struct TALER_EXCHANGE_BlindingPrepareHandle`
     208             :  * @param response_code HTTP response code, 0 on error
     209             :  * @param response parsed JSON result, NULL on error
     210             :  */
     211             : static void
     212          50 : handle_blinding_prepare_finished (void *cls,
     213             :                                   long response_code,
     214             :                                   const void *response)
     215             : {
     216          50 :   struct TALER_EXCHANGE_BlindingPrepareHandle *handle = cls;
     217          50 :   const json_t *j_response = response;
     218          50 :   struct TALER_EXCHANGE_BlindingPrepareResponse bpr = {
     219             :     .hr = {
     220             :       .reply = j_response,
     221          50 :       .http_status = (unsigned int) response_code
     222             :     },
     223             :   };
     224             : 
     225          50 :   handle->job = NULL;
     226             : 
     227          50 :   switch (response_code)
     228             :   {
     229           0 :   case 0:
     230           0 :     bpr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     231           0 :     break;
     232             : 
     233          50 :   case MHD_HTTP_OK:
     234             :     {
     235          50 :       if (GNUNET_OK !=
     236          50 :           blinding_prepare_ok (handle,
     237             :                                &bpr))
     238             :       {
     239           0 :         GNUNET_break_op (0);
     240           0 :         bpr.hr.http_status = 0;
     241           0 :         bpr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     242           0 :         break;
     243             :       }
     244             :     }
     245          50 :     TALER_EXCHANGE_blinding_prepare_cancel (handle);
     246          50 :     return;
     247             : 
     248           0 :   case MHD_HTTP_BAD_REQUEST:
     249             :     /* This should never happen, either us or the exchange is buggy
     250             :        (or API version conflict); just pass JSON reply to the application */
     251           0 :     bpr.hr.ec = TALER_JSON_get_error_code (j_response);
     252           0 :     bpr.hr.hint = TALER_JSON_get_error_hint (j_response);
     253           0 :     break;
     254             : 
     255           0 :   case MHD_HTTP_NOT_FOUND:
     256             :     /* Nothing really to verify, the exchange basically just says
     257             :        that it doesn't know the /csr endpoint or denomination.
     258             :        Can happen if the exchange doesn't support Clause Schnorr.
     259             :        We should simply pass the JSON reply to the application. */
     260           0 :     bpr.hr.ec = TALER_JSON_get_error_code (j_response);
     261           0 :     bpr.hr.hint = TALER_JSON_get_error_hint (j_response);
     262           0 :     break;
     263             : 
     264           0 :   case MHD_HTTP_GONE:
     265             :     /* could happen if denomination was revoked */
     266             :     /* Note: one might want to check /keys for revocation
     267             :        signature here, alas tricky in case our /keys
     268             :        is outdated => left to clients */
     269           0 :     bpr.hr.ec = TALER_JSON_get_error_code (j_response);
     270           0 :     bpr.hr.hint = TALER_JSON_get_error_hint (j_response);
     271           0 :     break;
     272             : 
     273           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     274             :     /* Server had an internal issue; we should retry, but this API
     275             :        leaves this to the application */
     276           0 :     bpr.hr.ec = TALER_JSON_get_error_code (j_response);
     277           0 :     bpr.hr.hint = TALER_JSON_get_error_hint (j_response);
     278           0 :     break;
     279             : 
     280           0 :   default:
     281             :     /* unexpected response code */
     282           0 :     GNUNET_break_op (0);
     283           0 :     bpr.hr.ec = TALER_JSON_get_error_code (j_response);
     284           0 :     bpr.hr.hint = TALER_JSON_get_error_hint (j_response);
     285           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     286             :                 "Unexpected response code %u/%d for the blinding-prepare request\n",
     287             :                 (unsigned int) response_code,
     288             :                 (int) bpr.hr.ec);
     289           0 :     break;
     290             : 
     291             :   }
     292             : 
     293           0 :   handle->callback (handle->callback_cls,
     294             :                     &bpr);
     295           0 :   handle->callback = NULL;
     296           0 :   TALER_EXCHANGE_blinding_prepare_cancel (handle);
     297             : }
     298             : 
     299             : 
     300             : struct TALER_EXCHANGE_BlindingPrepareHandle *
     301          50 : TALER_EXCHANGE_blinding_prepare (
     302             :   struct GNUNET_CURL_Context *curl_ctx,
     303             :   const char *exchange_url,
     304             :   const struct TALER_BlindingMasterSeedP *seed,
     305             :   bool for_melt,
     306             :   size_t num,
     307             :   const struct TALER_EXCHANGE_NonceKey nonce_keys[static num],
     308             :   TALER_EXCHANGE_BlindingPrepareCallback callback,
     309             :   void *callback_cls)
     310          50 : {
     311             :   struct TALER_EXCHANGE_BlindingPrepareHandle *bph;
     312             : 
     313          50 :   if (0 == num)
     314             :   {
     315           0 :     GNUNET_break (0);
     316           0 :     return NULL;
     317             :   }
     318         148 :   for (unsigned int i = 0; i<num; i++)
     319          98 :     if (GNUNET_CRYPTO_BSA_CS !=
     320          98 :         nonce_keys[i].pk->key.bsign_pub_key->cipher)
     321             :     {
     322           0 :       GNUNET_break (0);
     323           0 :       return NULL;
     324             :     }
     325          50 :   bph = GNUNET_new (struct TALER_EXCHANGE_BlindingPrepareHandle);
     326          50 :   bph->num = num;
     327          50 :   bph->callback = callback;
     328          50 :   bph->for_melt = for_melt;
     329          50 :   bph->callback_cls = callback_cls;
     330          50 :   bph->url = TALER_url_join (exchange_url,
     331             :                              "blinding-prepare",
     332             :                              NULL);
     333          50 :   if (NULL == bph->url)
     334             :   {
     335           0 :     GNUNET_break (0);
     336           0 :     GNUNET_free (bph);
     337           0 :     return NULL;
     338             :   }
     339             :   {
     340             :     CURL *eh;
     341             :     json_t *j_nks;
     342          50 :     json_t *j_request = GNUNET_JSON_PACK (
     343             :       GNUNET_JSON_pack_string ("cipher",
     344             :                                "CS"),
     345             :       GNUNET_JSON_pack_string ("operation",
     346             :                                for_melt ? "melt" : "withdraw"),
     347             :       GNUNET_JSON_pack_data_auto ("seed",
     348             :                                   seed));
     349          50 :     GNUNET_assert (NULL != j_request);
     350             : 
     351          50 :     j_nks = json_array ();
     352          50 :     GNUNET_assert (NULL != j_nks);
     353             : 
     354         148 :     for (size_t i = 0; i<num; i++)
     355             :     {
     356          98 :       const struct TALER_EXCHANGE_NonceKey *nk = &nonce_keys[i];
     357          98 :       json_t *j_entry = GNUNET_JSON_PACK (
     358             :         GNUNET_JSON_pack_uint64 ("coin_offset",
     359             :                                  nk->cnc_num),
     360             :         GNUNET_JSON_pack_data_auto ("denom_pub_hash",
     361             :                                     &nk->pk->h_key));
     362             : 
     363          98 :       GNUNET_assert (NULL != j_entry);
     364          98 :       GNUNET_assert (0 ==
     365             :                      json_array_append_new (j_nks,
     366             :                                             j_entry));
     367             :     }
     368          50 :     GNUNET_assert (0 ==
     369             :                    json_object_set_new (j_request,
     370             :                                         "nks",
     371             :                                         j_nks));
     372          50 :     eh = TALER_EXCHANGE_curl_easy_get_ (bph->url);
     373         100 :     if ( (NULL == eh) ||
     374             :          (GNUNET_OK !=
     375          50 :           TALER_curl_easy_post (&bph->post_ctx,
     376             :                                 eh,
     377             :                                 j_request)))
     378             :     {
     379           0 :       GNUNET_break (0);
     380           0 :       if (NULL != eh)
     381           0 :         curl_easy_cleanup (eh);
     382           0 :       json_decref (j_request);
     383           0 :       GNUNET_free (bph->url);
     384           0 :       GNUNET_free (bph);
     385           0 :       return NULL;
     386             :     }
     387             : 
     388          50 :     json_decref (j_request);
     389         100 :     bph->job = GNUNET_CURL_job_add2 (curl_ctx,
     390             :                                      eh,
     391          50 :                                      bph->post_ctx.headers,
     392             :                                      &handle_blinding_prepare_finished,
     393             :                                      bph);
     394          50 :     if (NULL == bph->job)
     395             :     {
     396           0 :       GNUNET_break (0);
     397           0 :       TALER_EXCHANGE_blinding_prepare_cancel (bph);
     398           0 :       return NULL;
     399             :     }
     400             :   }
     401          50 :   return bph;
     402             : }
     403             : 
     404             : 
     405             : void
     406         125 : TALER_EXCHANGE_blinding_prepare_cancel (
     407             :   struct TALER_EXCHANGE_BlindingPrepareHandle *bph)
     408             : {
     409         125 :   if (NULL == bph)
     410          75 :     return;
     411          50 :   if (NULL != bph->job)
     412             :   {
     413           0 :     GNUNET_CURL_job_cancel (bph->job);
     414           0 :     bph->job = NULL;
     415             :   }
     416          50 :   GNUNET_free (bph->url);
     417          50 :   TALER_curl_easy_post_finished (&bph->post_ctx);
     418          50 :   GNUNET_free (bph);
     419             : }
     420             : 
     421             : 
     422             : /* end of lib/exchange_api_blinding_prepare.c */

Generated by: LCOV version 1.16