LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_refresh_reveal.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 0 294 0.0 %
Date: 2019-12-06 06:02:21 Functions: 0 8 0.0 %

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

Generated by: LCOV version 1.14