LCOV - code coverage report
Current view: top level - exchange-lib - exchange_api_refresh_link.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 87 124 70.2 %
Date: 2017-09-17 17:24:28 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2015, 2016 GNUnet e.V.
       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 exchange-lib/exchange_api_refresh_link.c
      19             :  * @brief Implementation of the /refresh/link request of the exchange's HTTP API
      20             :  * @author Christian Grothoff
      21             :  */
      22             : #include "platform.h"
      23             : #include <curl/curl.h>
      24             : #include <microhttpd.h> /* just for HTTP status codes */
      25             : #include <gnunet/gnunet_util_lib.h>
      26             : #include <gnunet/gnunet_curl_lib.h>
      27             : #include "taler_exchange_service.h"
      28             : #include "taler_json_lib.h"
      29             : #include "exchange_api_handle.h"
      30             : #include "taler_signatures.h"
      31             : 
      32             : 
      33             : /**
      34             :  * @brief A /refresh/link Handle
      35             :  */
      36             : struct TALER_EXCHANGE_RefreshLinkHandle
      37             : {
      38             : 
      39             :   /**
      40             :    * The connection to exchange this request handle will use
      41             :    */
      42             :   struct TALER_EXCHANGE_Handle *exchange;
      43             : 
      44             :   /**
      45             :    * The url for this request.
      46             :    */
      47             :   char *url;
      48             : 
      49             :   /**
      50             :    * Handle for the request.
      51             :    */
      52             :   struct GNUNET_CURL_Job *job;
      53             : 
      54             :   /**
      55             :    * Function to call with the result.
      56             :    */
      57             :   TALER_EXCHANGE_RefreshLinkCallback link_cb;
      58             : 
      59             :   /**
      60             :    * Closure for @e cb.
      61             :    */
      62             :   void *link_cb_cls;
      63             : 
      64             :   /**
      65             :    * Private key of the coin, required to decode link information.
      66             :    */
      67             :   struct TALER_CoinSpendPrivateKeyP coin_priv;
      68             : 
      69             : };
      70             : 
      71             : 
      72             : /**
      73             :  * Parse the provided linkage data from the "200 OK" response
      74             :  * for one of the coins.
      75             :  *
      76             :  * @param rlh refresh link handle
      77             :  * @param json json reply with the data for one coin
      78             :  * @param coin_num number of the coin to decode
      79             :  * @param trans_pub our transfer public key
      80             :  * @param[out] coin_priv where to return private coin key
      81             :  * @param[out] sig where to return private coin signature
      82             :  * @param[out] pub where to return the public key for the coin
      83             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
      84             :  */
      85             : static int
      86          17 : parse_refresh_link_coin (const struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
      87             :                          const json_t *json,
      88             :                          unsigned int coin_num,
      89             :                          const struct TALER_TransferPublicKeyP *trans_pub,
      90             :                          struct TALER_CoinSpendPrivateKeyP *coin_priv,
      91             :                          struct TALER_DenominationSignature *sig,
      92             :                          struct TALER_DenominationPublicKey *pub)
      93             : {
      94             :   struct GNUNET_CRYPTO_RsaSignature *bsig;
      95             :   struct GNUNET_CRYPTO_RsaPublicKey *rpub;
      96          17 :   struct GNUNET_JSON_Specification spec[] = {
      97             :     GNUNET_JSON_spec_rsa_public_key ("denom_pub", &rpub),
      98             :     GNUNET_JSON_spec_rsa_signature ("ev_sig", &bsig),
      99             :     GNUNET_JSON_spec_end()
     100             :   };
     101             :   struct TALER_TransferSecretP secret;
     102             :   struct TALER_FreshCoinP fc;
     103             : 
     104             :   /* parse reply */
     105          17 :   if (GNUNET_OK !=
     106          17 :       GNUNET_JSON_parse (json,
     107             :                          spec,
     108             :                          NULL, NULL))
     109             :   {
     110           0 :     GNUNET_break_op (0);
     111           0 :     return GNUNET_SYSERR;
     112             :   }
     113             : 
     114          17 :   TALER_link_recover_transfer_secret (trans_pub,
     115             :                                       &rlh->coin_priv,
     116             :                                       &secret);
     117          17 :   TALER_setup_fresh_coin (&secret,
     118             :                           coin_num,
     119             :                           &fc);
     120             : 
     121             :   /* extract coin and signature */
     122          17 :   *coin_priv = fc.coin_priv;
     123             :   sig->rsa_signature
     124          17 :     = GNUNET_CRYPTO_rsa_unblind (bsig,
     125             :                                  &fc.blinding_key.bks,
     126             :                                  rpub);
     127             :   /* clean up */
     128          17 :   pub->rsa_public_key = GNUNET_CRYPTO_rsa_public_key_dup (rpub);
     129          17 :   GNUNET_JSON_parse_free (spec);
     130          17 :   return GNUNET_OK;
     131             : }
     132             : 
     133             : 
     134             : /**
     135             :  * Parse the provided linkage data from the "200 OK" response
     136             :  * for one of the coins.
     137             :  *
     138             :  * @param[in,out] rlh refresh link handle (callback may be zero'ed out)
     139             :  * @param json json reply with the data for one coin
     140             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
     141             :  */
     142             : static int
     143           1 : parse_refresh_link_ok (struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
     144             :                        const json_t *json)
     145             : {
     146             :   unsigned int session;
     147             :   unsigned int num_coins;
     148             :   int ret;
     149             : 
     150           1 :   if (! json_is_array (json))
     151             :   {
     152           0 :     GNUNET_break_op (0);
     153           0 :     return GNUNET_SYSERR;
     154             :   }
     155           1 :   num_coins = 0;
     156             :   /* Theoretically, a coin may have been melted repeatedly
     157             :      into different sessions; so the response is an array
     158             :      which contains information by melting session.  That
     159             :      array contains another array.  However, our API returns
     160             :      a single 1d array, so we flatten the 2d array that is
     161             :      returned into a single array. Note that usually a coin
     162             :      is melted at most once, and so we'll only run this
     163             :      loop once for 'session=0' in most cases.
     164             : 
     165             :      num_coins tracks the size of the 1d array we return,
     166             :      whilst 'i' and 'session' track the 2d array. */
     167           4 :   for (session=0;session<json_array_size (json); session++)
     168             :   {
     169             :     json_t *jsona;
     170           1 :     struct GNUNET_JSON_Specification spec[] = {
     171             :       GNUNET_JSON_spec_json ("new_coins", &jsona),
     172             :       GNUNET_JSON_spec_end()
     173             :     };
     174             : 
     175           1 :     if (GNUNET_OK !=
     176           1 :         GNUNET_JSON_parse (json_array_get (json,
     177             :                                            session),
     178             :                            spec,
     179             :                            NULL, NULL))
     180             :     {
     181           0 :       GNUNET_break_op (0);
     182           0 :       return GNUNET_SYSERR;
     183             :     }
     184           1 :     if (! json_is_array (jsona))
     185             :     {
     186           0 :       GNUNET_break_op (0);
     187           0 :       GNUNET_JSON_parse_free (spec);
     188           0 :       return GNUNET_SYSERR;
     189             :     }
     190             : 
     191             :     /* count all coins over all sessions */
     192           1 :     num_coins += json_array_size (jsona);
     193           1 :     GNUNET_JSON_parse_free (spec);
     194             :   }
     195             :   /* Now that we know how big the 1d array is, allocate
     196             :      and fill it. */
     197           1 :   {
     198             :     unsigned int off_coin; /* index into 1d array */
     199             :     unsigned int i;
     200           1 :     struct TALER_CoinSpendPrivateKeyP coin_privs[num_coins];
     201           1 :     struct TALER_DenominationSignature sigs[num_coins];
     202           1 :     struct TALER_DenominationPublicKey pubs[num_coins];
     203             : 
     204           1 :     memset (sigs, 0, sizeof (sigs));
     205           1 :     memset (pubs, 0, sizeof (pubs));
     206           1 :     off_coin = 0;
     207           4 :     for (session=0;session<json_array_size (json); session++)
     208             :     {
     209             :       json_t *jsona;
     210             :       struct TALER_TransferPublicKeyP trans_pub;
     211           1 :       struct GNUNET_JSON_Specification spec[] = {
     212             :         GNUNET_JSON_spec_json ("new_coins",
     213             :                                &jsona),
     214             :         GNUNET_JSON_spec_fixed_auto ("transfer_pub",
     215             :                                      &trans_pub),
     216             :         GNUNET_JSON_spec_end()
     217             :       };
     218             : 
     219           1 :       if (GNUNET_OK !=
     220           1 :           GNUNET_JSON_parse (json_array_get (json,
     221             :                                              session),
     222             :                              spec,
     223             :                              NULL, NULL))
     224             :       {
     225           0 :         GNUNET_break_op (0);
     226           0 :         return GNUNET_SYSERR;
     227             :       }
     228           1 :       if (! json_is_array (jsona))
     229             :       {
     230           0 :         GNUNET_break_op (0);
     231           0 :         GNUNET_JSON_parse_free (spec);
     232           0 :         return GNUNET_SYSERR;
     233             :       }
     234             : 
     235             :       /* decode all coins */
     236          18 :       for (i=0;i<json_array_size (jsona);i++)
     237             :       {
     238          17 :         if (GNUNET_OK !=
     239          68 :             parse_refresh_link_coin (rlh,
     240          17 :                                      json_array_get (jsona,
     241             :                                                      i),
     242             :                                      i,
     243             :                                      &trans_pub,
     244          17 :                                      &coin_privs[i+off_coin],
     245          17 :                                      &sigs[i+off_coin],
     246          17 :                                      &pubs[i+off_coin]))
     247             :         {
     248           0 :           GNUNET_break_op (0);
     249           0 :           break;
     250             :         }
     251             :       }
     252             :       /* check if we really got all, then invoke callback */
     253           1 :       off_coin += i;
     254           1 :       if (i != json_array_size (jsona))
     255             :       {
     256           0 :         GNUNET_break_op (0);
     257           0 :         ret = GNUNET_SYSERR;
     258           0 :         GNUNET_JSON_parse_free (spec);
     259           0 :         break;
     260             :       }
     261           1 :       GNUNET_JSON_parse_free (spec);
     262             :     } /* end of for (session) */
     263             : 
     264           1 :     if (off_coin == num_coins)
     265             :     {
     266           1 :       rlh->link_cb (rlh->link_cb_cls,
     267             :                     MHD_HTTP_OK,
     268             :                     TALER_EC_NONE,
     269             :                     num_coins,
     270             :                     coin_privs,
     271             :                     sigs,
     272             :                     pubs,
     273             :                     json);
     274           1 :       rlh->link_cb = NULL;
     275           1 :       ret = GNUNET_OK;
     276             :     }
     277             :     else
     278             :     {
     279           0 :       GNUNET_break_op (0);
     280           0 :       ret = GNUNET_SYSERR;
     281             :     }
     282             : 
     283             :     /* clean up */
     284          18 :     for (i=0;i<off_coin;i++)
     285             :     {
     286          17 :       if (NULL != sigs[i].rsa_signature)
     287          17 :         GNUNET_CRYPTO_rsa_signature_free (sigs[i].rsa_signature);
     288          17 :       if (NULL != pubs[i].rsa_public_key)
     289          17 :         GNUNET_CRYPTO_rsa_public_key_free (pubs[i].rsa_public_key);
     290             :     }
     291             :   }
     292           1 :   return ret;
     293             : }
     294             : 
     295             : 
     296             : /**
     297             :  * Function called when we're done processing the
     298             :  * HTTP /refresh/link request.
     299             :  *
     300             :  * @param cls the `struct TALER_EXCHANGE_RefreshLinkHandle`
     301             :  * @param response_code HTTP response code, 0 on error
     302             :  * @param json parsed JSON result, NULL on error
     303             :  */
     304             : static void
     305           1 : handle_refresh_link_finished (void *cls,
     306             :                               long response_code,
     307             :                               const json_t *json)
     308             : {
     309           1 :   struct TALER_EXCHANGE_RefreshLinkHandle *rlh = cls;
     310             : 
     311           1 :   rlh->job = NULL;
     312           1 :   switch (response_code)
     313             :   {
     314             :   case 0:
     315           0 :     break;
     316             :   case MHD_HTTP_OK:
     317           1 :     if (GNUNET_OK !=
     318           1 :         parse_refresh_link_ok (rlh,
     319             :                                json))
     320             :     {
     321           0 :       GNUNET_break_op (0);
     322           0 :       response_code = 0;
     323             :     }
     324           1 :     break;
     325             :   case MHD_HTTP_BAD_REQUEST:
     326             :     /* This should never happen, either us or the exchange is buggy
     327             :        (or API version conflict); just pass JSON reply to the application */
     328           0 :     break;
     329             :   case MHD_HTTP_NOT_FOUND:
     330             :     /* Nothing really to verify, exchange says this coin was not melted; we
     331             :        should pass the JSON reply to the application */
     332           0 :     break;
     333             :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     334             :     /* Server had an internal issue; we should retry, but this API
     335             :        leaves this to the application */
     336           0 :     break;
     337             :   default:
     338             :     /* unexpected response code */
     339           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     340             :                 "Unexpected response code %u\n",
     341             :                 (unsigned int) response_code);
     342           0 :     GNUNET_break (0);
     343           0 :     response_code = 0;
     344           0 :     break;
     345             :   }
     346           1 :   if (NULL != rlh->link_cb)
     347           0 :     rlh->link_cb (rlh->link_cb_cls,
     348             :                   response_code,
     349             :                   TALER_JSON_get_error_code (json),
     350             :                   0,
     351             :                   NULL,
     352             :                   NULL,
     353             :                   NULL,
     354             :                   json);
     355           1 :   TALER_EXCHANGE_refresh_link_cancel (rlh);
     356           1 : }
     357             : 
     358             : 
     359             : /**
     360             :  * Submit a link request to the exchange and get the exchange's response.
     361             :  *
     362             :  * This API is typically not used by anyone, it is more a threat
     363             :  * against those trying to receive a funds transfer by abusing the
     364             :  * /refresh protocol.
     365             :  *
     366             :  * @param exchange the exchange handle; the exchange must be ready to operate
     367             :  * @param coin_priv private key to request link data for
     368             :  * @param link_cb the callback to call with the useful result of the
     369             :  *        refresh operation the @a coin_priv was involved in (if any)
     370             :  * @param link_cb_cls closure for @a link_cb
     371             :  * @return a handle for this request
     372             :  */
     373             : struct TALER_EXCHANGE_RefreshLinkHandle *
     374           1 : TALER_EXCHANGE_refresh_link (struct TALER_EXCHANGE_Handle *exchange,
     375             :                              const struct TALER_CoinSpendPrivateKeyP *coin_priv,
     376             :                              TALER_EXCHANGE_RefreshLinkCallback link_cb,
     377             :                              void *link_cb_cls)
     378             : {
     379             :   struct TALER_EXCHANGE_RefreshLinkHandle *rlh;
     380             :   CURL *eh;
     381             :   struct GNUNET_CURL_Context *ctx;
     382             :   struct TALER_CoinSpendPublicKeyP coin_pub;
     383             :   char *pub_str;
     384             :   char *arg_str;
     385             : 
     386           1 :   if (GNUNET_YES !=
     387           1 :       MAH_handle_is_ready (exchange))
     388             :   {
     389           0 :     GNUNET_break (0);
     390           0 :     return NULL;
     391             :   }
     392             : 
     393           1 :   GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
     394             :                                       &coin_pub.eddsa_pub);
     395           1 :   pub_str = GNUNET_STRINGS_data_to_string_alloc (&coin_pub,
     396             :                                                  sizeof (struct TALER_CoinSpendPublicKeyP));
     397           1 :   GNUNET_asprintf (&arg_str,
     398             :                    "/refresh/link?coin_pub=%s",
     399             :                    pub_str);
     400           1 :   GNUNET_free (pub_str);
     401             : 
     402           1 :   rlh = GNUNET_new (struct TALER_EXCHANGE_RefreshLinkHandle);
     403           1 :   rlh->exchange = exchange;
     404           1 :   rlh->link_cb = link_cb;
     405           1 :   rlh->link_cb_cls = link_cb_cls;
     406           1 :   rlh->coin_priv = *coin_priv;
     407           1 :   rlh->url = MAH_path_to_url (exchange, arg_str);
     408           1 :   GNUNET_free (arg_str);
     409             : 
     410           1 :   eh = curl_easy_init ();
     411           1 :   GNUNET_assert (CURLE_OK ==
     412             :                  curl_easy_setopt (eh,
     413             :                                    CURLOPT_URL,
     414             :                                    rlh->url));
     415           1 :   ctx = MAH_handle_to_context (exchange);
     416           1 :   rlh->job = GNUNET_CURL_job_add (ctx,
     417             :                           eh,
     418             :                           GNUNET_YES,
     419             :                           &handle_refresh_link_finished,
     420             :                           rlh);
     421           1 :   return rlh;
     422             : }
     423             : 
     424             : 
     425             : /**
     426             :  * Cancel a refresh link request.  This function cannot be used
     427             :  * on a request handle if the callback was already invoked.
     428             :  *
     429             :  * @param rlh the refresh link handle
     430             :  */
     431             : void
     432           1 : TALER_EXCHANGE_refresh_link_cancel (struct TALER_EXCHANGE_RefreshLinkHandle *rlh)
     433             : {
     434           1 :   if (NULL != rlh->job)
     435             :   {
     436           0 :     GNUNET_CURL_job_cancel (rlh->job);
     437           0 :     rlh->job = NULL;
     438             :   }
     439           1 :   GNUNET_free (rlh->url);
     440           1 :   GNUNET_free (rlh);
     441           1 : }
     442             : 
     443             : 
     444             : /* end of exchange_api_refresh_link.c */

Generated by: LCOV version 1.13