LCOV - code coverage report
Current view: top level - exchange-lib - exchange_api_common.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 52 95 54.7 %
Date: 2017-09-17 17:24:28 Functions: 1 1 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2015-2017 Inria & GNUnet e.V.
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU General Public License along with
      14             :   TALER; see the file COPYING.  If not, see
      15             :   <http://www.gnu.org/licenses/>
      16             : */
      17             : /**
      18             :  * @file exchange-lib/exchange_api_common.c
      19             :  * @brief common functions for the exchange API
      20             :  * @author Christian Grothoff
      21             :  */
      22             : #include "platform.h"
      23             : #include "taler_json_lib.h"
      24             : #include <gnunet/gnunet_curl_lib.h>
      25             : #include "exchange_api_handle.h"
      26             : #include "taler_signatures.h"
      27             : 
      28             : 
      29             : /**
      30             :  * Verify a coins transaction history as returned by the exchange.
      31             :  *
      32             :  * @param currency expected currency for the coin
      33             :  * @param coin_pub public key of the coin
      34             :  * @param history history of the coin in json encoding
      35             :  * @param[out] total how much of the coin has been spent according to @a history
      36             :  * @return #GNUNET_OK if @a history is valid, #GNUNET_SYSERR if not
      37             :  */
      38             : int
      39           5 : TALER_EXCHANGE_verify_coin_history (const char *currency,
      40             :                                     const struct TALER_CoinSpendPublicKeyP *coin_pub,
      41             :                                     json_t *history,
      42             :                                     struct TALER_Amount *total)
      43             : {
      44             :   size_t len;
      45             :   size_t off;
      46             :   int add;
      47             :   struct TALER_Amount rtotal;
      48             : 
      49           5 :   if (NULL == history)
      50             :   {
      51           0 :     GNUNET_break_op (0);
      52           0 :     return GNUNET_SYSERR;
      53             :   }
      54           5 :   len = json_array_size (history);
      55           5 :   if (0 == len)
      56             :   {
      57           0 :     GNUNET_break_op (0);
      58           0 :     return GNUNET_SYSERR;
      59             :   }
      60           5 :   TALER_amount_get_zero (currency,
      61             :                          total);
      62           5 :   TALER_amount_get_zero (currency,
      63             :                          &rtotal);
      64          24 :   for (off=0;off<len;off++)
      65             :   {
      66             :     json_t *transaction;
      67             :     struct TALER_Amount amount;
      68             :     const char *type;
      69           7 :     struct GNUNET_JSON_Specification spec_glob[] = {
      70             :       TALER_JSON_spec_amount ("amount",
      71             :                               &amount),
      72             :       GNUNET_JSON_spec_string ("type",
      73             :                                &type),
      74             :       GNUNET_JSON_spec_end()
      75             :     };
      76             : 
      77           7 :     transaction = json_array_get (history,
      78             :                                   off);
      79           7 :     if (GNUNET_OK !=
      80           7 :         GNUNET_JSON_parse (transaction,
      81             :                            spec_glob,
      82             :                            NULL, NULL))
      83             :     {
      84           0 :       GNUNET_break_op (0);
      85           0 :       return GNUNET_SYSERR;
      86             :     }
      87           7 :     add = GNUNET_SYSERR;
      88           7 :     if (0 == strcasecmp (type,
      89             :                          "DEPOSIT"))
      90             :     {
      91             :       struct TALER_DepositRequestPS dr;
      92             :       struct TALER_CoinSpendSignatureP sig;
      93           5 :       struct GNUNET_JSON_Specification spec[] = {
      94             :         GNUNET_JSON_spec_fixed_auto ("coin_sig",
      95             :                                      &sig),
      96             :         GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
      97             :                                      &dr.h_contract_terms),
      98             :         GNUNET_JSON_spec_fixed_auto ("h_wire",
      99             :                                      &dr.h_wire),
     100             :         GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
     101             :                                             &dr.timestamp),
     102             :         GNUNET_JSON_spec_absolute_time_nbo ("refund_deadline",
     103             :                                             &dr.refund_deadline),
     104             :         TALER_JSON_spec_amount_nbo ("deposit_fee",
     105             :                                     &dr.deposit_fee),
     106             :         GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     107             :                                      &dr.merchant),
     108             :         GNUNET_JSON_spec_end()
     109             :       };
     110             : 
     111           5 :       if (GNUNET_OK !=
     112           5 :           GNUNET_JSON_parse (transaction,
     113             :                              spec,
     114             :                              NULL, NULL))
     115             :       {
     116           0 :         GNUNET_break_op (0);
     117           0 :         return GNUNET_SYSERR;
     118             :       }
     119           5 :       dr.purpose.size = htonl (sizeof (dr));
     120           5 :       dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
     121           5 :       TALER_amount_hton (&dr.amount_with_fee,
     122             :                          &amount);
     123           5 :       dr.coin_pub = *coin_pub;
     124           5 :       if (GNUNET_OK !=
     125           5 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT,
     126             :                                       &dr.purpose,
     127             :                                       &sig.eddsa_signature,
     128             :                                       &coin_pub->eddsa_pub))
     129             :       {
     130           0 :         GNUNET_break_op (0);
     131           0 :         return GNUNET_SYSERR;
     132             :       }
     133             :       /* TODO: check that deposit fee and coin value match
     134             :          our expectations from /keys! */
     135           5 :       add = GNUNET_YES;
     136             :     }
     137           2 :     else if (0 == strcasecmp (type,
     138             :                               "MELT"))
     139             :     {
     140             :       struct TALER_RefreshMeltCoinAffirmationPS rm;
     141             :       struct TALER_CoinSpendSignatureP sig;
     142           1 :       struct GNUNET_JSON_Specification spec[] = {
     143             :         GNUNET_JSON_spec_fixed_auto ("coin_sig",
     144             :                                      &sig),
     145             :         GNUNET_JSON_spec_fixed_auto ("session_hash",
     146             :                                      &rm.session_hash),
     147             :         TALER_JSON_spec_amount_nbo ("melt_fee",
     148             :                                     &rm.melt_fee),
     149             :         GNUNET_JSON_spec_end()
     150             :       };
     151             : 
     152           1 :       if (GNUNET_OK !=
     153           1 :           GNUNET_JSON_parse (transaction,
     154             :                              spec,
     155             :                              NULL, NULL))
     156             :       {
     157           0 :         GNUNET_break_op (0);
     158           0 :         return GNUNET_SYSERR;
     159             :       }
     160           1 :       rm.purpose.size = htonl (sizeof (rm));
     161           1 :       rm.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
     162           1 :       TALER_amount_hton (&rm.amount_with_fee,
     163             :                          &amount);
     164           1 :       rm.coin_pub = *coin_pub;
     165           1 :       if (GNUNET_OK !=
     166           1 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
     167             :                                       &rm.purpose,
     168             :                                       &sig.eddsa_signature,
     169             :                                       &coin_pub->eddsa_pub))
     170             :       {
     171           0 :         GNUNET_break_op (0);
     172           0 :         return GNUNET_SYSERR;
     173             :       }
     174             :       /* TODO: check that deposit fee and coin value match
     175             :          our expectations from /keys! */
     176           1 :       add = GNUNET_YES;
     177             :     }
     178           1 :     else if (0 == strcasecmp (type,
     179             :                               "REFUND"))
     180             :     {
     181             :       struct TALER_RefundRequestPS rr;
     182             :       struct TALER_MerchantSignatureP sig;
     183           0 :       struct GNUNET_JSON_Specification spec[] = {
     184             :         GNUNET_JSON_spec_fixed_auto ("merchant_sig",
     185             :                                      &sig),
     186             :         GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
     187             :                                      &rr.h_contract_terms),
     188             :         GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     189             :                                      &rr.merchant),
     190             :         GNUNET_JSON_spec_uint64 ("rtransaction_id",
     191             :                                  &rr.rtransaction_id),
     192             :         TALER_JSON_spec_amount_nbo ("refund_fee",
     193             :                                     &rr.refund_fee),
     194             :         GNUNET_JSON_spec_end()
     195             :       };
     196             : 
     197           0 :       if (GNUNET_OK !=
     198           0 :           GNUNET_JSON_parse (transaction,
     199             :                              spec,
     200             :                              NULL, NULL))
     201             :       {
     202           0 :         GNUNET_break_op (0);
     203           0 :         return GNUNET_SYSERR;
     204             :       }
     205           0 :       rr.purpose.size = htonl (sizeof (rr));
     206           0 :       rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
     207           0 :       rr.coin_pub = *coin_pub;
     208           0 :       TALER_amount_hton (&rr.refund_amount,
     209             :                          &amount);
     210           0 :       if (GNUNET_OK !=
     211           0 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
     212             :                                       &rr.purpose,
     213             :                                       &sig.eddsa_sig,
     214             :                                       &rr.merchant.eddsa_pub))
     215             :       {
     216           0 :         GNUNET_break_op (0);
     217           0 :         return GNUNET_SYSERR;
     218             :       }
     219             :       /* NOTE: theoretically, we could also check that the given
     220             :          merchant_pub and h_contract_terms appear in the
     221             :          history under deposits.  However, there is really no benefit
     222             :          for the exchange to lie here, so not checking is probably OK
     223             :          (an auditor ought to check, though). Then again, we similarly
     224             :          had no reason to check the merchant's signature (other than a
     225             :          well-formendess check). */
     226             :       /* TODO: check that deposit fee and coin value match
     227             :          our expectations from /keys! */
     228           0 :       add = GNUNET_NO;
     229             :     }
     230           1 :     else if (0 == strcasecmp (type,
     231             :                               "PAYBACK"))
     232             :     {
     233             :       struct TALER_PaybackConfirmationPS pc;
     234             :       struct TALER_ExchangePublicKeyP exchange_pub;
     235             :       struct TALER_ExchangeSignatureP exchange_sig;
     236           1 :       struct GNUNET_JSON_Specification spec[] = {
     237             :         GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     238             :                                      &exchange_sig),
     239             :         GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     240             :                                      &exchange_pub),
     241             :         GNUNET_JSON_spec_fixed_auto ("reserve_pub",
     242             :                                      &pc.reserve_pub),
     243             :         GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
     244             :                                             &pc.timestamp),
     245             :         GNUNET_JSON_spec_end()
     246             :       };
     247             : 
     248           1 :       if (GNUNET_OK !=
     249           1 :           GNUNET_JSON_parse (transaction,
     250             :                              spec,
     251             :                              NULL, NULL))
     252             :       {
     253           0 :         GNUNET_break_op (0);
     254           0 :         return GNUNET_SYSERR;
     255             :       }
     256           1 :       pc.purpose.size = htonl (sizeof (pc));
     257           1 :       pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
     258           1 :       pc.coin_pub = *coin_pub;
     259           1 :       TALER_amount_hton (&pc.payback_amount,
     260             :                          &amount);
     261           1 :       if (GNUNET_OK !=
     262           1 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK,
     263             :                                       &pc.purpose,
     264             :                                       &exchange_sig.eddsa_signature,
     265             :                                       &exchange_pub.eddsa_pub))
     266             :       {
     267           0 :         GNUNET_break_op (0);
     268           0 :         return GNUNET_SYSERR;
     269             :       }
     270           1 :       add = GNUNET_YES;
     271             :     }
     272             :     else
     273             :     {
     274             :       /* signature not supported, new version on server? */
     275           0 :       GNUNET_break_op (0);
     276           0 :       return GNUNET_SYSERR;
     277             :     }
     278           7 :     if (GNUNET_YES == add)
     279             :     {
     280             :       /* This amount should be added to the total */
     281           7 :       if (GNUNET_OK !=
     282           7 :           TALER_amount_add (total,
     283             :                             total,
     284             :                             &amount))
     285             :       {
     286             :         /* overflow in history already!? inconceivable! Bad exchange! */
     287           0 :         GNUNET_break_op (0);
     288           0 :         return GNUNET_SYSERR;
     289             :       }
     290             :     }
     291             :     else
     292             :     {
     293             :       /* This amount should be subtracted from the total.
     294             : 
     295             :          However, for the implementation, we first *add* up all of
     296             :          these negative amounts, as we might get refunds before
     297             :          deposits from a semi-evil exchange.  Then, at the end, we do
     298             :          the subtraction by calculating "total = total - rtotal" */
     299           0 :       GNUNET_assert (GNUNET_NO == add);
     300           0 :       if (GNUNET_OK !=
     301           0 :           TALER_amount_add (&rtotal,
     302             :                             &rtotal,
     303             :                             &amount))
     304             :       {
     305             :         /* overflow in refund history? inconceivable! Bad exchange! */
     306           0 :         GNUNET_break_op (0);
     307           0 :         return GNUNET_SYSERR;
     308             :       }
     309             :     }
     310             :   }
     311             : 
     312             :   /* Finally, subtract 'rtotal' from total to handle the subtractions */
     313           5 :   if (GNUNET_OK !=
     314           5 :       TALER_amount_subtract (total,
     315             :                              total,
     316             :                              &rtotal))
     317             :   {
     318             :     /* underflow in history? inconceivable! Bad exchange! */
     319           0 :     GNUNET_break_op (0);
     320           0 :     return GNUNET_SYSERR;
     321             :   }
     322             : 
     323           5 :   return GNUNET_OK;
     324             : }
     325             : 
     326             : 
     327             : /* end of exchange_api_common.c */

Generated by: LCOV version 1.13