LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_refreshes_reveal.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 209 289 72.3 %
Date: 2021-08-30 06:43:37 Functions: 8 8 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2019, 2021 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Affero 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 Affero General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU Affero General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file taler-exchange-httpd_refreshes_reveal.c
      18             :  * @brief Handle /refreshes/$RCH/reveal requests
      19             :  * @author Florian Dold
      20             :  * @author Benedikt Mueller
      21             :  * @author Christian Grothoff
      22             :  */
      23             : #include "platform.h"
      24             : #include <gnunet/gnunet_util_lib.h>
      25             : #include <jansson.h>
      26             : #include <microhttpd.h>
      27             : #include "taler_mhd_lib.h"
      28             : #include "taler-exchange-httpd_mhd.h"
      29             : #include "taler-exchange-httpd_refreshes_reveal.h"
      30             : #include "taler-exchange-httpd_responses.h"
      31             : #include "taler-exchange-httpd_keys.h"
      32             : 
      33             : 
      34             : /**
      35             :  * Maximum number of fresh coins we allow per refresh operation.
      36             :  */
      37             : #define MAX_FRESH_COINS 256
      38             : 
      39             : /**
      40             :  * How often do we at most retry the reveal transaction sequence?
      41             :  * Twice should really suffice in all cases (as the possible conflict
      42             :  * cannot happen more than once).
      43             :  */
      44             : #define MAX_REVEAL_RETRIES 2
      45             : 
      46             : 
      47             : /**
      48             :  * Send a response for "/refreshes/$RCH/reveal".
      49             :  *
      50             :  * @param connection the connection to send the response to
      51             :  * @param num_freshcoins number of new coins for which we reveal data
      52             :  * @param sigs array of @a num_freshcoins signatures revealed
      53             :  * @return a MHD result code
      54             :  */
      55             : static MHD_RESULT
      56           7 : reply_refreshes_reveal_success (struct MHD_Connection *connection,
      57             :                                 unsigned int num_freshcoins,
      58             :                                 const struct TALER_DenominationSignature *sigs)
      59             : {
      60             :   json_t *list;
      61             : 
      62           7 :   list = json_array ();
      63           7 :   GNUNET_assert (NULL != list);
      64          26 :   for (unsigned int freshcoin_index = 0;
      65             :        freshcoin_index < num_freshcoins;
      66          19 :        freshcoin_index++)
      67             :   {
      68             :     json_t *obj;
      69             : 
      70          19 :     obj = GNUNET_JSON_PACK (
      71             :       GNUNET_JSON_pack_rsa_signature ("ev_sig",
      72             :                                       sigs[freshcoin_index].rsa_signature));
      73          19 :     GNUNET_assert (0 ==
      74             :                    json_array_append_new (list,
      75             :                                           obj));
      76             :   }
      77             : 
      78           7 :   return TALER_MHD_REPLY_JSON_PACK (
      79             :     connection,
      80             :     MHD_HTTP_OK,
      81             :     GNUNET_JSON_pack_array_steal ("ev_sigs",
      82             :                                   list));
      83             : }
      84             : 
      85             : 
      86             : /**
      87             :  * State for a /refreshes/$RCH/reveal operation.
      88             :  */
      89             : struct RevealContext
      90             : {
      91             : 
      92             :   /**
      93             :    * Commitment of the refresh operation.
      94             :    */
      95             :   struct TALER_RefreshCommitmentP rc;
      96             : 
      97             :   /**
      98             :    * Transfer public key at gamma.
      99             :    */
     100             :   struct TALER_TransferPublicKeyP gamma_tp;
     101             : 
     102             :   /**
     103             :    * Transfer private keys revealed to us.
     104             :    */
     105             :   struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
     106             : 
     107             :   /**
     108             :    * Denominations being requested.
     109             :    */
     110             :   const struct TEH_DenominationKey **dks;
     111             : 
     112             :   /**
     113             :    * Envelopes to be signed.
     114             :    */
     115             :   const struct TALER_RefreshCoinData *rcds;
     116             : 
     117             :   /**
     118             :    * Signatures over the link data (of type
     119             :    * #TALER_SIGNATURE_WALLET_COIN_LINK)
     120             :    */
     121             :   const struct TALER_CoinSpendSignatureP *link_sigs;
     122             : 
     123             :   /**
     124             :    * Envelopes with the signatures to be returned.  Initially NULL.
     125             :    */
     126             :   struct TALER_DenominationSignature *ev_sigs;
     127             : 
     128             :   /**
     129             :    * Size of the @e dks, @e rcds and @e ev_sigs arrays (if non-NULL).
     130             :    */
     131             :   unsigned int num_fresh_coins;
     132             : 
     133             :   /**
     134             :    * Result from preflight checks. #GNUNET_NO for no result,
     135             :    * #GNUNET_YES if preflight found previous successful operation,
     136             :    * #GNUNET_SYSERR if prefight check failed hard (and generated
     137             :    * an MHD response already).
     138             :    */
     139             :   int preflight_ok;
     140             : 
     141             : };
     142             : 
     143             : 
     144             : /**
     145             :  * Function called with information about a refresh order we already
     146             :  * persisted.  Stores the result in @a cls so we don't do the calculation
     147             :  * again.
     148             :  *
     149             :  * @param cls closure with a `struct RevealContext`
     150             :  * @param num_freshcoins size of the @a rrcs array
     151             :  * @param rrcs array of @a num_freshcoins information about coins to be created
     152             :  * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1
     153             :  * @param tprivs array of @e num_tprivs transfer private keys
     154             :  * @param tp transfer public key information
     155             :  */
     156             : static void
     157           1 : check_exists_cb (void *cls,
     158             :                  uint32_t num_freshcoins,
     159             :                  const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs,
     160             :                  unsigned int num_tprivs,
     161             :                  const struct TALER_TransferPrivateKeyP *tprivs,
     162             :                  const struct TALER_TransferPublicKeyP *tp)
     163             : {
     164           1 :   struct RevealContext *rctx = cls;
     165             : 
     166           1 :   if (0 == num_freshcoins)
     167             :   {
     168           0 :     GNUNET_break (0);
     169           0 :     return;
     170             :   }
     171             :   /* This should be a database invariant for us */
     172           1 :   GNUNET_break (TALER_CNC_KAPPA - 1 == num_tprivs);
     173             :   /* Given that the $RCH value matched, we don't actually need to check these
     174             :      values (we checked before). However, if a client repeats a request with
     175             :      invalid values the 2nd time, that's a protocol violation we should at least
     176             :      log (but it's safe to ignore it). */
     177           1 :   GNUNET_break_op (0 ==
     178             :                    GNUNET_memcmp (tp,
     179             :                                   &rctx->gamma_tp));
     180           1 :   GNUNET_break_op (0 ==
     181             :                    memcmp (tprivs,
     182             :                            &rctx->transfer_privs,
     183             :                            sizeof (struct TALER_TransferPrivateKeyP)
     184             :                            * num_tprivs));
     185             :   /* We usually sign early (optimistic!), but in case we change that *and*
     186             :      we do find the operation in the database, we could use this: */
     187           1 :   if (NULL == rctx->ev_sigs)
     188             :   {
     189           0 :     rctx->ev_sigs = GNUNET_new_array (num_freshcoins,
     190             :                                       struct TALER_DenominationSignature);
     191           0 :     for (unsigned int i = 0; i<num_freshcoins; i++)
     192           0 :       rctx->ev_sigs[i].rsa_signature
     193           0 :         = GNUNET_CRYPTO_rsa_signature_dup (rrcs[i].coin_sig.rsa_signature);
     194             :   }
     195             : }
     196             : 
     197             : 
     198             : /**
     199             :  * Check if the "/refreshes/$RCH/reveal" request was already successful
     200             :  * before.  If so, just return the old result.
     201             :  *
     202             :  * @param cls closure of type `struct RevealContext`
     203             :  * @param connection MHD request which triggered the transaction
     204             :  * @param[out] mhd_ret set to MHD response status for @a connection,
     205             :  *             if transaction failed (!)
     206             :  * @return transaction status
     207             :  */
     208             : static enum GNUNET_DB_QueryStatus
     209           8 : refreshes_reveal_preflight (void *cls,
     210             :                             struct MHD_Connection *connection,
     211             :                             MHD_RESULT *mhd_ret)
     212             : {
     213           8 :   struct RevealContext *rctx = cls;
     214             :   enum GNUNET_DB_QueryStatus qs;
     215             : 
     216             :   /* Try to see if we already have given an answer before. */
     217           8 :   qs = TEH_plugin->get_refresh_reveal (TEH_plugin->cls,
     218           8 :                                        &rctx->rc,
     219             :                                        &check_exists_cb,
     220             :                                        rctx);
     221           8 :   switch (qs)
     222             :   {
     223           7 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     224           7 :     return qs; /* continue normal execution */
     225           0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
     226           0 :     return qs;
     227           0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     228           0 :     GNUNET_break (qs);
     229           0 :     *mhd_ret = TALER_MHD_reply_with_error (connection,
     230             :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     231             :                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
     232             :                                            "refresh reveal");
     233           0 :     rctx->preflight_ok = GNUNET_SYSERR;
     234           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     235           1 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     236             :   default:
     237             :     /* Hossa, already found our reply! */
     238           1 :     GNUNET_assert (NULL != rctx->ev_sigs);
     239           1 :     rctx->preflight_ok = GNUNET_YES;
     240           1 :     return qs;
     241             :   }
     242             : }
     243             : 
     244             : 
     245             : /**
     246             :  * Execute a "/refreshes/$RCH/reveal".  The client is revealing to us the
     247             :  * transfer keys for @a #TALER_CNC_KAPPA-1 sets of coins.  Verify that the
     248             :  * revealed transfer keys would allow linkage to the blinded coins.
     249             :  *
     250             :  * IF it returns a non-error code, the transaction logic MUST
     251             :  * NOT queue a MHD response.  IF it returns an hard error, the
     252             :  * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF
     253             :  * it returns the soft error code, the function MAY be called again to
     254             :  * retry and MUST not queue a MHD response.
     255             :  *
     256             :  * @param cls closure of type `struct RevealContext`
     257             :  * @param connection MHD request which triggered the transaction
     258             :  * @param[out] mhd_ret set to MHD response status for @a connection,
     259             :  *             if transaction failed (!)
     260             :  * @return transaction status
     261             :  */
     262             : static enum GNUNET_DB_QueryStatus
     263           7 : refreshes_reveal_transaction (void *cls,
     264             :                               struct MHD_Connection *connection,
     265             :                               MHD_RESULT *mhd_ret)
     266             : {
     267           7 :   struct RevealContext *rctx = cls;
     268             :   struct TALER_EXCHANGEDB_Melt melt;
     269             :   enum GNUNET_DB_QueryStatus qs;
     270             : 
     271             :   /* Obtain basic information about the refresh operation and what
     272             :      gamma we committed to. */
     273           7 :   qs = TEH_plugin->get_melt (TEH_plugin->cls,
     274           7 :                              &rctx->rc,
     275             :                              &melt);
     276           7 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     277             :   {
     278           0 :     *mhd_ret = TALER_MHD_reply_with_error (connection,
     279             :                                            MHD_HTTP_NOT_FOUND,
     280             :                                            TALER_EC_EXCHANGE_REFRESHES_REVEAL_SESSION_UNKNOWN,
     281             :                                            NULL);
     282           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     283             :   }
     284           7 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     285           0 :     return qs;
     286           7 :   if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
     287           7 :        (melt.session.noreveal_index >= TALER_CNC_KAPPA) )
     288             :   {
     289           0 :     GNUNET_break (0);
     290           0 :     *mhd_ret = TALER_MHD_reply_with_error (connection,
     291             :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     292             :                                            TALER_EC_GENERIC_DB_FETCH_FAILED,
     293             :                                            "melt");
     294           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     295             :   }
     296             : 
     297             :   /* Verify commitment */
     298             :   {
     299             :     /* Note that the contents of rcs[melt.session.noreveal_index]
     300             :        will be aliased and are *not* allocated (or deallocated) in
     301             :        this function -- in contrast to the other offsets! */
     302             :     struct TALER_RefreshCommitmentEntry rcs[TALER_CNC_KAPPA];
     303             :     struct TALER_RefreshCommitmentP rc_expected;
     304             :     unsigned int off;
     305             : 
     306           7 :     off = 0; /* did we pass session.noreveal_index yet? */
     307          28 :     for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
     308             :     {
     309          21 :       struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
     310             : 
     311          21 :       if (i == melt.session.noreveal_index)
     312             :       {
     313             :         /* Take these coin envelopes from the client */
     314           7 :         rce->transfer_pub = rctx->gamma_tp;
     315           7 :         rce->new_coins = (struct TALER_RefreshCoinData *) rctx->rcds;
     316           7 :         off = 1;
     317             :       }
     318             :       else
     319             :       {
     320             :         /* Reconstruct coin envelopes from transfer private key */
     321          14 :         const struct TALER_TransferPrivateKeyP *tpriv
     322          14 :           = &rctx->transfer_privs[i - off];
     323             :         struct TALER_TransferSecretP ts;
     324             : 
     325          14 :         GNUNET_CRYPTO_ecdhe_key_get_public (&tpriv->ecdhe_priv,
     326             :                                             &rce->transfer_pub.ecdhe_pub);
     327          14 :         TALER_link_reveal_transfer_secret (tpriv,
     328             :                                            &melt.session.coin.coin_pub,
     329             :                                            &ts);
     330          14 :         rce->new_coins = GNUNET_new_array (rctx->num_fresh_coins,
     331             :                                            struct TALER_RefreshCoinData);
     332          52 :         for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
     333             :         {
     334          38 :           struct TALER_RefreshCoinData *rcd = &rce->new_coins[j];
     335             :           struct TALER_PlanchetSecretsP ps;
     336             :           struct TALER_PlanchetDetail pd;
     337             :           struct GNUNET_HashCode c_hash;
     338             : 
     339          38 :           rcd->dk = &rctx->dks[j]->denom_pub;
     340          38 :           TALER_planchet_setup_refresh (&ts,
     341             :                                         j,
     342             :                                         &ps);
     343          38 :           GNUNET_assert (GNUNET_OK ==
     344             :                          TALER_planchet_prepare (rcd->dk,
     345             :                                                  &ps,
     346             :                                                  &c_hash,
     347             :                                                  &pd));
     348          38 :           rcd->coin_ev = pd.coin_ev;
     349          38 :           rcd->coin_ev_size = pd.coin_ev_size;
     350             :         }
     351             :       }
     352             :     }
     353           7 :     TALER_refresh_get_commitment (&rc_expected,
     354             :                                   TALER_CNC_KAPPA,
     355             :                                   rctx->num_fresh_coins,
     356             :                                   rcs,
     357             :                                   &melt.session.coin.coin_pub,
     358             :                                   &melt.session.amount_with_fee);
     359             : 
     360             :     /* Free resources allocated above */
     361          28 :     for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
     362             :     {
     363          21 :       struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
     364             : 
     365          21 :       if (i == melt.session.noreveal_index)
     366           7 :         continue; /* This offset is special: not allocated! */
     367          52 :       for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
     368             :       {
     369          38 :         struct TALER_RefreshCoinData *rcd = &rce->new_coins[j];
     370             : 
     371          38 :         GNUNET_free (rcd->coin_ev);
     372             :       }
     373          14 :       GNUNET_free (rce->new_coins);
     374             :     }
     375             : 
     376             :     /* Verify rc_expected matches rc */
     377           7 :     if (0 != GNUNET_memcmp (&rctx->rc,
     378             :                             &rc_expected))
     379             :     {
     380           1 :       GNUNET_break_op (0);
     381           1 :       *mhd_ret = TALER_MHD_REPLY_JSON_PACK (
     382             :         connection,
     383             :         MHD_HTTP_CONFLICT,
     384             :         TALER_JSON_pack_ec (
     385             :           TALER_EC_EXCHANGE_REFRESHES_REVEAL_COMMITMENT_VIOLATION),
     386             :         GNUNET_JSON_pack_data_auto ("rc_expected",
     387             :                                     &rc_expected));
     388           1 :       return GNUNET_DB_STATUS_HARD_ERROR;
     389             :     }
     390             :   } /* end of checking "rc_expected" */
     391             : 
     392             :   /* check amounts add up! */
     393             :   {
     394             :     struct TALER_Amount refresh_cost;
     395             : 
     396           6 :     refresh_cost = melt.melt_fee;
     397          21 :     for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
     398             :     {
     399             :       struct TALER_Amount total;
     400             : 
     401          15 :       if ( (0 >
     402          15 :             TALER_amount_add (&total,
     403          15 :                               &rctx->dks[i]->meta.fee_withdraw,
     404          30 :                               &rctx->dks[i]->meta.value)) ||
     405             :            (0 >
     406          15 :             TALER_amount_add (&refresh_cost,
     407             :                               &refresh_cost,
     408             :                               &total)) )
     409             :       {
     410           0 :         GNUNET_break_op (0);
     411           0 :         *mhd_ret = TALER_MHD_reply_with_error (connection,
     412             :                                                MHD_HTTP_INTERNAL_SERVER_ERROR,
     413             :                                                TALER_EC_EXCHANGE_REFRESHES_REVEAL_COST_CALCULATION_OVERFLOW,
     414             :                                                NULL);
     415           0 :         return GNUNET_DB_STATUS_HARD_ERROR;
     416             :       }
     417             :     }
     418           6 :     if (0 < TALER_amount_cmp (&refresh_cost,
     419             :                               &melt.session.amount_with_fee))
     420             :     {
     421           0 :       GNUNET_break_op (0);
     422           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     423             :                                              MHD_HTTP_BAD_REQUEST,
     424             :                                              TALER_EC_EXCHANGE_REFRESHES_REVEAL_AMOUNT_INSUFFICIENT,
     425             :                                              NULL);
     426           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     427             :     }
     428             :   }
     429           6 :   return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     430             : }
     431             : 
     432             : 
     433             : /**
     434             :  * Persist result of a "/refreshes/$RCH/reveal" operation.
     435             :  *
     436             :  * @param cls closure of type `struct RevealContext`
     437             :  * @param connection MHD request which triggered the transaction
     438             :  * @param[out] mhd_ret set to MHD response status for @a connection,
     439             :  *             if transaction failed (!)
     440             :  * @return transaction status
     441             :  */
     442             : static enum GNUNET_DB_QueryStatus
     443           6 : refreshes_reveal_persist (void *cls,
     444             :                           struct MHD_Connection *connection,
     445             :                           MHD_RESULT *mhd_ret)
     446             : {
     447           6 :   struct RevealContext *rctx = cls;
     448             :   enum GNUNET_DB_QueryStatus qs;
     449             : 
     450             :   /* Persist operation result in DB */
     451           6 :   {
     452           6 :     struct TALER_EXCHANGEDB_RefreshRevealedCoin rrcs[rctx->num_fresh_coins];
     453             : 
     454          21 :     for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
     455             :     {
     456          15 :       struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
     457             : 
     458          15 :       rrc->denom_pub = rctx->dks[i]->denom_pub;
     459          15 :       rrc->orig_coin_link_sig = rctx->link_sigs[i];
     460          15 :       rrc->coin_ev = rctx->rcds[i].coin_ev;
     461          15 :       rrc->coin_ev_size = rctx->rcds[i].coin_ev_size;
     462          15 :       rrc->coin_sig = rctx->ev_sigs[i];
     463             :     }
     464           6 :     qs = TEH_plugin->insert_refresh_reveal (TEH_plugin->cls,
     465           6 :                                             &rctx->rc,
     466             :                                             rctx->num_fresh_coins,
     467             :                                             rrcs,
     468             :                                             TALER_CNC_KAPPA - 1,
     469           6 :                                             rctx->transfer_privs,
     470           6 :                                             &rctx->gamma_tp);
     471             :   }
     472           6 :   if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     473             :   {
     474           0 :     *mhd_ret = TALER_MHD_reply_with_error (connection,
     475             :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     476             :                                            TALER_EC_GENERIC_DB_STORE_FAILED,
     477             :                                            "refresh_reveal");
     478             :   }
     479           6 :   return qs;
     480             : }
     481             : 
     482             : 
     483             : /**
     484             :  * Resolve denomination hashes.
     485             :  *
     486             :  * @param connection the MHD connection to handle
     487             :  * @param rctx context for the operation, partially built at this time
     488             :  * @param link_sigs_json link signatures in JSON format
     489             :  * @param new_denoms_h_json requests for fresh coins to be created
     490             :  * @param coin_evs envelopes of gamma-selected coins to be signed
     491             :  * @return MHD result code
     492             :  */
     493             : static MHD_RESULT
     494           8 : resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
     495             :                                         struct RevealContext *rctx,
     496             :                                         const json_t *link_sigs_json,
     497             :                                         const json_t *new_denoms_h_json,
     498             :                                         const json_t *coin_evs)
     499           8 : {
     500           8 :   unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
     501             :   /* We know num_fresh_coins is bounded by #MAX_FRESH_COINS, so this is safe */
     502           8 :   const struct TEH_DenominationKey *dks[num_fresh_coins];
     503           8 :   struct GNUNET_HashCode dk_h[num_fresh_coins];
     504           8 :   struct TALER_RefreshCoinData rcds[num_fresh_coins];
     505           8 :   struct TALER_CoinSpendSignatureP link_sigs[num_fresh_coins];
     506             :   struct TALER_EXCHANGEDB_Melt melt;
     507             :   enum GNUNET_GenericReturnValue res;
     508             :   MHD_RESULT ret;
     509             :   struct TEH_KeyStateHandle *ksh;
     510             : 
     511           8 :   ksh = TEH_keys_get_state ();
     512           8 :   if (NULL == ksh)
     513             :   {
     514           0 :     return TALER_MHD_reply_with_error (connection,
     515             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     516             :                                        TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
     517             :                                        NULL);
     518             :   }
     519             :   /* Parse denomination key hashes */
     520          31 :   for (unsigned int i = 0; i<num_fresh_coins; i++)
     521             :   {
     522             :     struct GNUNET_JSON_Specification spec[] = {
     523          23 :       GNUNET_JSON_spec_fixed_auto (NULL,
     524             :                                    &dk_h[i]),
     525          23 :       GNUNET_JSON_spec_end ()
     526             :     };
     527             :     MHD_RESULT mret;
     528             : 
     529          23 :     res = TALER_MHD_parse_json_array (connection,
     530             :                                       new_denoms_h_json,
     531             :                                       spec,
     532             :                                       i,
     533             :                                       -1);
     534          23 :     if (GNUNET_OK != res)
     535             :     {
     536           0 :       return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
     537             :     }
     538          46 :     dks[i] = TEH_keys_denomination_by_hash2 (ksh,
     539          23 :                                              &dk_h[i],
     540             :                                              connection,
     541             :                                              &mret);
     542          23 :     if (NULL == dks[i])
     543           0 :       return mret;
     544             : 
     545          23 :     if (GNUNET_TIME_absolute_is_past (dks[i]->meta.expire_withdraw))
     546             :     {
     547             :       struct GNUNET_TIME_Absolute now;
     548             : 
     549           0 :       now = GNUNET_TIME_absolute_get ();
     550           0 :       (void) GNUNET_TIME_round_abs (&now);
     551             :       /* This denomination is past the expiration time for withdraws */
     552           0 :       return TEH_RESPONSE_reply_expired_denom_pub_hash (
     553             :         connection,
     554           0 :         &dk_h[i],
     555             :         now,
     556             :         TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
     557             :         "REVEAL");
     558             :     }
     559          23 :     if (GNUNET_TIME_absolute_is_future (dks[i]->meta.start))
     560             :     {
     561             :       struct GNUNET_TIME_Absolute now;
     562             : 
     563           0 :       now = GNUNET_TIME_absolute_get ();
     564           0 :       (void) GNUNET_TIME_round_abs (&now);
     565             :       /* This denomination is not yet valid */
     566           0 :       return TEH_RESPONSE_reply_expired_denom_pub_hash (
     567             :         connection,
     568           0 :         &dk_h[i],
     569             :         now,
     570             :         TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
     571             :         "REVEAL");
     572             :     }
     573          23 :     if (dks[i]->recoup_possible)
     574             :     {
     575             :       /* This denomination has been revoked */
     576           0 :       return TALER_MHD_reply_with_error (
     577             :         connection,
     578             :         MHD_HTTP_GONE,
     579             :         TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
     580             :         NULL);
     581             :     }
     582             :   }
     583             : 
     584             :   /* Parse coin envelopes */
     585          31 :   for (unsigned int i = 0; i<num_fresh_coins; i++)
     586             :   {
     587          23 :     struct TALER_RefreshCoinData *rcd = &rcds[i];
     588             :     struct GNUNET_JSON_Specification spec[] = {
     589          23 :       GNUNET_JSON_spec_varsize (NULL,
     590             :                                 &rcd->coin_ev,
     591             :                                 &rcd->coin_ev_size),
     592          23 :       GNUNET_JSON_spec_end ()
     593             :     };
     594             : 
     595          23 :     res = TALER_MHD_parse_json_array (connection,
     596             :                                       coin_evs,
     597             :                                       spec,
     598             :                                       i,
     599             :                                       -1);
     600          23 :     if (GNUNET_OK != res)
     601             :     {
     602           0 :       for (unsigned int j = 0; j<i; j++)
     603           0 :         GNUNET_free (rcds[j].coin_ev);
     604           0 :       return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
     605             :     }
     606          23 :     rcd->dk = &dks[i]->denom_pub;
     607             :   }
     608             : 
     609             :   /* lookup old_coin_pub in database */
     610             :   {
     611             :     enum GNUNET_DB_QueryStatus qs;
     612             : 
     613           8 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     614           8 :         (qs = TEH_plugin->get_melt (TEH_plugin->cls,
     615           8 :                                     &rctx->rc,
     616             :                                     &melt)))
     617             :     {
     618           0 :       switch (qs)
     619             :       {
     620           0 :       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     621           0 :         ret = TALER_MHD_reply_with_error (connection,
     622             :                                           MHD_HTTP_NOT_FOUND,
     623             :                                           TALER_EC_EXCHANGE_REFRESHES_REVEAL_SESSION_UNKNOWN,
     624             :                                           NULL);
     625           0 :         break;
     626           0 :       case GNUNET_DB_STATUS_HARD_ERROR:
     627           0 :         ret = TALER_MHD_reply_with_error (connection,
     628             :                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
     629             :                                           TALER_EC_GENERIC_DB_FETCH_FAILED,
     630             :                                           "melt");
     631           0 :         break;
     632           0 :       case GNUNET_DB_STATUS_SOFT_ERROR:
     633             :       default:
     634           0 :         GNUNET_break (0);   /* should be impossible */
     635           0 :         ret = TALER_MHD_reply_with_error (connection,
     636             :                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
     637             :                                           TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
     638             :                                           NULL);
     639           0 :         break;
     640             :       }
     641           0 :       goto cleanup;
     642             :     }
     643             :   }
     644             :   /* Parse link signatures array */
     645          31 :   for (unsigned int i = 0; i<num_fresh_coins; i++)
     646             :   {
     647             :     struct GNUNET_JSON_Specification link_spec[] = {
     648          23 :       GNUNET_JSON_spec_fixed_auto (NULL, &link_sigs[i]),
     649          23 :       GNUNET_JSON_spec_end ()
     650             :     };
     651             : 
     652          23 :     res = TALER_MHD_parse_json_array (connection,
     653             :                                       link_sigs_json,
     654             :                                       link_spec,
     655             :                                       i,
     656             :                                       -1);
     657          23 :     if (GNUNET_OK != res)
     658           0 :       return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
     659             :     /* Check link_sigs[i] signature */
     660          23 :     if (GNUNET_OK !=
     661          23 :         TALER_wallet_link_verify (
     662          23 :           &dk_h[i],
     663          23 :           &rctx->gamma_tp,
     664          23 :           rcds[i].coin_ev,
     665             :           rcds[i].coin_ev_size,
     666             :           &melt.session.coin.coin_pub,
     667          23 :           &link_sigs[i]))
     668             :     {
     669           0 :       GNUNET_break_op (0);
     670           0 :       ret = TALER_MHD_reply_with_error (
     671             :         connection,
     672             :         MHD_HTTP_FORBIDDEN,
     673             :         TALER_EC_EXCHANGE_REFRESHES_REVEAL_LINK_SIGNATURE_INVALID,
     674             :         NULL);
     675           0 :       goto cleanup;
     676             :     }
     677             :   }
     678             : 
     679           8 :   rctx->num_fresh_coins = num_fresh_coins;
     680           8 :   rctx->rcds = rcds;
     681           8 :   rctx->dks = dks;
     682           8 :   rctx->link_sigs = link_sigs;
     683             : 
     684             :   /* sign _early_ (optimistic!) to keep out of transaction scope! */
     685           8 :   rctx->ev_sigs = GNUNET_new_array (rctx->num_fresh_coins,
     686             :                                     struct TALER_DenominationSignature);
     687          31 :   for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
     688             :   {
     689             :     enum TALER_ErrorCode ec;
     690             : 
     691          23 :     rctx->ev_sigs[i]
     692          23 :       = TEH_keys_denomination_sign (
     693          23 :           &dk_h[i],
     694          23 :           rctx->rcds[i].coin_ev,
     695          23 :           rctx->rcds[i].coin_ev_size,
     696             :           &ec);
     697          23 :     if (NULL == rctx->ev_sigs[i].rsa_signature)
     698             :     {
     699           0 :       GNUNET_break (0);
     700           0 :       ret = TALER_MHD_reply_with_ec (connection,
     701             :                                      ec,
     702             :                                      NULL);
     703           0 :       goto cleanup;
     704             :     }
     705             :   }
     706             : 
     707             :   /* We try the three transactions a few times, as theoretically
     708             :      the pre-check might be satisfied by a concurrent transaction
     709             :      voiding our final commit due to uniqueness violation; naturally,
     710             :      on hard errors we exit immediately */
     711           8 :   for (unsigned int retries = 0; retries < MAX_REVEAL_RETRIES; retries++)
     712             :   {
     713             :     /* do transactional work */
     714           8 :     rctx->preflight_ok = GNUNET_NO;
     715           8 :     if ( (GNUNET_OK ==
     716           8 :           TEH_DB_run_transaction (connection,
     717             :                                   "reveal pre-check",
     718             :                                   &ret,
     719             :                                   &refreshes_reveal_preflight,
     720           8 :                                   rctx)) &&
     721           8 :          (GNUNET_YES == rctx->preflight_ok) )
     722             :     {
     723             :       /* Generate final (positive) response */
     724           1 :       GNUNET_assert (NULL != rctx->ev_sigs);
     725           2 :       ret = reply_refreshes_reveal_success (connection,
     726             :                                             num_fresh_coins,
     727           1 :                                             rctx->ev_sigs);
     728           1 :       GNUNET_break (MHD_NO != ret);
     729           1 :       goto cleanup;   /* aka 'break' */
     730             :     }
     731           7 :     if (GNUNET_SYSERR == rctx->preflight_ok)
     732             :     {
     733           0 :       GNUNET_break (0);
     734           0 :       goto cleanup;   /* aka 'break' */
     735             :     }
     736           7 :     if (GNUNET_OK !=
     737           7 :         TEH_DB_run_transaction (connection,
     738             :                                 "run reveal",
     739             :                                 &ret,
     740             :                                 &refreshes_reveal_transaction,
     741             :                                 rctx))
     742             :     {
     743             :       /* reveal failed, too bad */
     744           1 :       GNUNET_break_op (0);
     745           1 :       goto cleanup;   /* aka 'break' */
     746             :     }
     747           6 :     if (GNUNET_OK ==
     748           6 :         TEH_DB_run_transaction (connection,
     749             :                                 "persist reveal",
     750             :                                 &ret,
     751             :                                 &refreshes_reveal_persist,
     752             :                                 rctx))
     753             :     {
     754             :       /* Generate final (positive) response */
     755           6 :       GNUNET_assert (NULL != rctx->ev_sigs);
     756          12 :       ret = reply_refreshes_reveal_success (connection,
     757             :                                             num_fresh_coins,
     758           6 :                                             rctx->ev_sigs);
     759           6 :       break;
     760             :     }
     761             :     /* If we get here, the final transaction failed, possibly
     762             :        due to a conflict between the pre-flight and us persisting
     763             :        the result, so we go again. */
     764             :   }   /* end for (retries...) */
     765             : 
     766           0 : cleanup:
     767           8 :   GNUNET_break (MHD_NO != ret);
     768             :   /* free resources */
     769           8 :   if (NULL != rctx->ev_sigs)
     770             :   {
     771          31 :     for (unsigned int i = 0; i<num_fresh_coins; i++)
     772          23 :       if (NULL != rctx->ev_sigs[i].rsa_signature)
     773          23 :         GNUNET_CRYPTO_rsa_signature_free (rctx->ev_sigs[i].rsa_signature);
     774           8 :     GNUNET_free (rctx->ev_sigs);
     775           8 :     rctx->ev_sigs = NULL; /* just to be safe... */
     776             :   }
     777          31 :   for (unsigned int i = 0; i<num_fresh_coins; i++)
     778          23 :     GNUNET_free (rcds[i].coin_ev);
     779           8 :   return ret;
     780             : }
     781             : 
     782             : 
     783             : /**
     784             :  * Handle a "/refreshes/$RCH/reveal" request.   Parses the given JSON
     785             :  * transfer private keys and if successful, passes everything to
     786             :  * #resolve_refreshes_reveal_denominations() which will verify that the
     787             :  * revealed information is valid then returns the signed refreshed
     788             :  * coins.
     789             :  *
     790             :  * @param connection the MHD connection to handle
     791             :  * @param rctx context for the operation, partially built at this time
     792             :  * @param tp_json private transfer keys in JSON format
     793             :  * @param link_sigs_json link signatures in JSON format
     794             :  * @param new_denoms_h_json requests for fresh coins to be created
     795             :  * @param coin_evs envelopes of gamma-selected coins to be signed
     796             :  * @return MHD result code
     797             :  */
     798             : static MHD_RESULT
     799           8 : handle_refreshes_reveal_json (struct MHD_Connection *connection,
     800             :                               struct RevealContext *rctx,
     801             :                               const json_t *tp_json,
     802             :                               const json_t *link_sigs_json,
     803             :                               const json_t *new_denoms_h_json,
     804             :                               const json_t *coin_evs)
     805             : {
     806           8 :   unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
     807           8 :   unsigned int num_tprivs = json_array_size (tp_json);
     808             : 
     809           8 :   GNUNET_assert (num_tprivs == TALER_CNC_KAPPA - 1); /* checked just earlier */
     810           8 :   if ( (num_fresh_coins >= MAX_FRESH_COINS) ||
     811             :        (0 == num_fresh_coins) )
     812             :   {
     813           0 :     GNUNET_break_op (0);
     814           0 :     return TALER_MHD_reply_with_error (connection,
     815             :                                        MHD_HTTP_BAD_REQUEST,
     816             :                                        TALER_EC_EXCHANGE_REFRESHES_REVEAL_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE,
     817             :                                        NULL);
     818             : 
     819             :   }
     820          16 :   if (json_array_size (new_denoms_h_json) !=
     821           8 :       json_array_size (coin_evs))
     822             :   {
     823           0 :     GNUNET_break_op (0);
     824           0 :     return TALER_MHD_reply_with_error (connection,
     825             :                                        MHD_HTTP_BAD_REQUEST,
     826             :                                        TALER_EC_EXCHANGE_REFRESHES_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISMATCH,
     827             :                                        "new_denoms/coin_evs");
     828             :   }
     829          16 :   if (json_array_size (new_denoms_h_json) !=
     830           8 :       json_array_size (link_sigs_json))
     831             :   {
     832           0 :     GNUNET_break_op (0);
     833           0 :     return TALER_MHD_reply_with_error (connection,
     834             :                                        MHD_HTTP_BAD_REQUEST,
     835             :                                        TALER_EC_EXCHANGE_REFRESHES_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISMATCH,
     836             :                                        "new_denoms/link_sigs");
     837             :   }
     838             : 
     839             :   /* Parse transfer private keys array */
     840          24 :   for (unsigned int i = 0; i<num_tprivs; i++)
     841             :   {
     842             :     struct GNUNET_JSON_Specification trans_spec[] = {
     843          16 :       GNUNET_JSON_spec_fixed_auto (NULL, &rctx->transfer_privs[i]),
     844          16 :       GNUNET_JSON_spec_end ()
     845             :     };
     846             :     enum GNUNET_GenericReturnValue res;
     847             : 
     848          16 :     res = TALER_MHD_parse_json_array (connection,
     849             :                                       tp_json,
     850             :                                       trans_spec,
     851             :                                       i,
     852             :                                       -1);
     853          16 :     if (GNUNET_OK != res)
     854           0 :       return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
     855             :   }
     856             : 
     857           8 :   return resolve_refreshes_reveal_denominations (connection,
     858             :                                                  rctx,
     859             :                                                  link_sigs_json,
     860             :                                                  new_denoms_h_json,
     861             :                                                  coin_evs);
     862             : }
     863             : 
     864             : 
     865             : MHD_RESULT
     866           8 : TEH_handler_reveal (struct TEH_RequestContext *rc,
     867             :                     const json_t *root,
     868             :                     const char *const args[2])
     869             : {
     870             :   json_t *coin_evs;
     871             :   json_t *transfer_privs;
     872             :   json_t *link_sigs;
     873             :   json_t *new_denoms_h;
     874             :   struct RevealContext rctx;
     875             :   struct GNUNET_JSON_Specification spec[] = {
     876           8 :     GNUNET_JSON_spec_fixed_auto ("transfer_pub", &rctx.gamma_tp),
     877           8 :     GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs),
     878           8 :     GNUNET_JSON_spec_json ("link_sigs", &link_sigs),
     879           8 :     GNUNET_JSON_spec_json ("coin_evs", &coin_evs),
     880           8 :     GNUNET_JSON_spec_json ("new_denoms_h", &new_denoms_h),
     881           8 :     GNUNET_JSON_spec_end ()
     882             :   };
     883             : 
     884           8 :   memset (&rctx,
     885             :           0,
     886             :           sizeof (rctx));
     887           8 :   if (GNUNET_OK !=
     888           8 :       GNUNET_STRINGS_string_to_data (args[0],
     889             :                                      strlen (args[0]),
     890             :                                      &rctx.rc,
     891             :                                      sizeof (rctx.rc)))
     892             :   {
     893           0 :     GNUNET_break_op (0);
     894           0 :     return TALER_MHD_reply_with_error (rc->connection,
     895             :                                        MHD_HTTP_BAD_REQUEST,
     896             :                                        TALER_EC_EXCHANGE_REFRESHES_REVEAL_INVALID_RCH,
     897             :                                        args[0]);
     898             :   }
     899           8 :   if (0 != strcmp (args[1],
     900             :                    "reveal"))
     901             :   {
     902           0 :     GNUNET_break_op (0);
     903           0 :     return TALER_MHD_reply_with_error (rc->connection,
     904             :                                        MHD_HTTP_BAD_REQUEST,
     905             :                                        TALER_EC_EXCHANGE_REFRESHES_REVEAL_OPERATION_INVALID,
     906           0 :                                        args[1]);
     907             :   }
     908             : 
     909             :   {
     910             :     enum GNUNET_GenericReturnValue res;
     911             : 
     912           8 :     res = TALER_MHD_parse_json_data (rc->connection,
     913             :                                      root,
     914             :                                      spec);
     915           8 :     if (GNUNET_OK != res)
     916             :     {
     917           0 :       GNUNET_break_op (0);
     918           0 :       return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
     919             :     }
     920             :   }
     921             : 
     922             :   /* Check we got enough transfer private keys */
     923             :   /* Note we do +1 as 1 row (cut-and-choose!) is missing! */
     924           8 :   if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1)
     925             :   {
     926           0 :     GNUNET_JSON_parse_free (spec);
     927           0 :     GNUNET_break_op (0);
     928           0 :     return TALER_MHD_reply_with_error (rc->connection,
     929             :                                        MHD_HTTP_BAD_REQUEST,
     930             :                                        TALER_EC_EXCHANGE_REFRESHES_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID,
     931             :                                        NULL);
     932             :   }
     933             : 
     934             :   {
     935             :     MHD_RESULT res;
     936             : 
     937           8 :     res = handle_refreshes_reveal_json (rc->connection,
     938             :                                         &rctx,
     939             :                                         transfer_privs,
     940             :                                         link_sigs,
     941             :                                         new_denoms_h,
     942             :                                         coin_evs);
     943           8 :     GNUNET_JSON_parse_free (spec);
     944           8 :     return res;
     945             :   }
     946             : }
     947             : 
     948             : 
     949             : /* end of taler-exchange-httpd_refreshes_reveal.c */

Generated by: LCOV version 1.14