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-11-25 11:31:41 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             :   int add;
      46             :   struct TALER_Amount rtotal;
      47             : 
      48           5 :   if (NULL == history)
      49             :   {
      50           0 :     GNUNET_break_op (0);
      51           0 :     return GNUNET_SYSERR;
      52             :   }
      53           5 :   len = json_array_size (history);
      54           5 :   if (0 == len)
      55             :   {
      56           0 :     GNUNET_break_op (0);
      57           0 :     return GNUNET_SYSERR;
      58             :   }
      59           5 :   GNUNET_assert (GNUNET_OK ==
      60             :                  TALER_amount_get_zero (currency,
      61             :                                         total));
      62           5 :   GNUNET_assert (GNUNET_OK ==
      63             :                  TALER_amount_get_zero (currency,
      64             :                                         &rtotal));
      65          24 :   for (size_t off=0;off<len;off++)
      66             :   {
      67             :     json_t *transaction;
      68             :     struct TALER_Amount amount;
      69             :     const char *type;
      70           7 :     struct GNUNET_JSON_Specification spec_glob[] = {
      71             :       TALER_JSON_spec_amount ("amount",
      72             :                               &amount),
      73             :       GNUNET_JSON_spec_string ("type",
      74             :                                &type),
      75             :       GNUNET_JSON_spec_end()
      76             :     };
      77             : 
      78           7 :     transaction = json_array_get (history,
      79             :                                   off);
      80           7 :     if (GNUNET_OK !=
      81           7 :         GNUNET_JSON_parse (transaction,
      82             :                            spec_glob,
      83             :                            NULL, NULL))
      84             :     {
      85           0 :       GNUNET_break_op (0);
      86           0 :       return GNUNET_SYSERR;
      87             :     }
      88           7 :     add = GNUNET_SYSERR;
      89           7 :     if (0 == strcasecmp (type,
      90             :                          "DEPOSIT"))
      91             :     {
      92             :       struct TALER_DepositRequestPS dr;
      93             :       struct TALER_CoinSpendSignatureP sig;
      94           5 :       struct GNUNET_JSON_Specification spec[] = {
      95             :         GNUNET_JSON_spec_fixed_auto ("coin_sig",
      96             :                                      &sig),
      97             :         GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
      98             :                                      &dr.h_contract_terms),
      99             :         GNUNET_JSON_spec_fixed_auto ("h_wire",
     100             :                                      &dr.h_wire),
     101             :         GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
     102             :                                             &dr.timestamp),
     103             :         GNUNET_JSON_spec_absolute_time_nbo ("refund_deadline",
     104             :                                             &dr.refund_deadline),
     105             :         TALER_JSON_spec_amount_nbo ("deposit_fee",
     106             :                                     &dr.deposit_fee),
     107             :         GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     108             :                                      &dr.merchant),
     109             :         GNUNET_JSON_spec_end()
     110             :       };
     111             : 
     112           5 :       if (GNUNET_OK !=
     113           5 :           GNUNET_JSON_parse (transaction,
     114             :                              spec,
     115             :                              NULL, NULL))
     116             :       {
     117           0 :         GNUNET_break_op (0);
     118           0 :         return GNUNET_SYSERR;
     119             :       }
     120           5 :       dr.purpose.size = htonl (sizeof (dr));
     121           5 :       dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
     122           5 :       TALER_amount_hton (&dr.amount_with_fee,
     123             :                          &amount);
     124           5 :       dr.coin_pub = *coin_pub;
     125           5 :       if (GNUNET_OK !=
     126           5 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT,
     127             :                                       &dr.purpose,
     128             :                                       &sig.eddsa_signature,
     129             :                                       &coin_pub->eddsa_pub))
     130             :       {
     131           0 :         GNUNET_break_op (0);
     132           0 :         return GNUNET_SYSERR;
     133             :       }
     134             :       /* TODO: check that deposit fee and coin value match
     135             :          our expectations from /keys! */
     136           5 :       add = GNUNET_YES;
     137             :     }
     138           2 :     else if (0 == strcasecmp (type,
     139             :                               "MELT"))
     140             :     {
     141             :       struct TALER_RefreshMeltCoinAffirmationPS rm;
     142             :       struct TALER_CoinSpendSignatureP sig;
     143           1 :       struct GNUNET_JSON_Specification spec[] = {
     144             :         GNUNET_JSON_spec_fixed_auto ("coin_sig",
     145             :                                      &sig),
     146             :         GNUNET_JSON_spec_fixed_auto ("session_hash",
     147             :                                      &rm.session_hash),
     148             :         TALER_JSON_spec_amount_nbo ("melt_fee",
     149             :                                     &rm.melt_fee),
     150             :         GNUNET_JSON_spec_end()
     151             :       };
     152             : 
     153           1 :       if (GNUNET_OK !=
     154           1 :           GNUNET_JSON_parse (transaction,
     155             :                              spec,
     156             :                              NULL, NULL))
     157             :       {
     158           0 :         GNUNET_break_op (0);
     159           0 :         return GNUNET_SYSERR;
     160             :       }
     161           1 :       rm.purpose.size = htonl (sizeof (rm));
     162           1 :       rm.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
     163           1 :       TALER_amount_hton (&rm.amount_with_fee,
     164             :                          &amount);
     165           1 :       rm.coin_pub = *coin_pub;
     166           1 :       if (GNUNET_OK !=
     167           1 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
     168             :                                       &rm.purpose,
     169             :                                       &sig.eddsa_signature,
     170             :                                       &coin_pub->eddsa_pub))
     171             :       {
     172           0 :         GNUNET_break_op (0);
     173           0 :         return GNUNET_SYSERR;
     174             :       }
     175             :       /* TODO: check that deposit fee and coin value match
     176             :          our expectations from /keys! */
     177           1 :       add = GNUNET_YES;
     178             :     }
     179           1 :     else if (0 == strcasecmp (type,
     180             :                               "REFUND"))
     181             :     {
     182             :       struct TALER_RefundRequestPS rr;
     183             :       struct TALER_MerchantSignatureP sig;
     184           0 :       struct GNUNET_JSON_Specification spec[] = {
     185             :         GNUNET_JSON_spec_fixed_auto ("merchant_sig",
     186             :                                      &sig),
     187             :         GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
     188             :                                      &rr.h_contract_terms),
     189             :         GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     190             :                                      &rr.merchant),
     191             :         GNUNET_JSON_spec_uint64 ("rtransaction_id",
     192             :                                  &rr.rtransaction_id),
     193             :         TALER_JSON_spec_amount_nbo ("refund_fee",
     194             :                                     &rr.refund_fee),
     195             :         GNUNET_JSON_spec_end()
     196             :       };
     197             : 
     198           0 :       if (GNUNET_OK !=
     199           0 :           GNUNET_JSON_parse (transaction,
     200             :                              spec,
     201             :                              NULL, NULL))
     202             :       {
     203           0 :         GNUNET_break_op (0);
     204           0 :         return GNUNET_SYSERR;
     205             :       }
     206           0 :       rr.purpose.size = htonl (sizeof (rr));
     207           0 :       rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
     208           0 :       rr.coin_pub = *coin_pub;
     209           0 :       TALER_amount_hton (&rr.refund_amount,
     210             :                          &amount);
     211           0 :       if (GNUNET_OK !=
     212           0 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
     213             :                                       &rr.purpose,
     214             :                                       &sig.eddsa_sig,
     215             :                                       &rr.merchant.eddsa_pub))
     216             :       {
     217           0 :         GNUNET_break_op (0);
     218           0 :         return GNUNET_SYSERR;
     219             :       }
     220             :       /* NOTE: theoretically, we could also check that the given
     221             :          merchant_pub and h_contract_terms appear in the
     222             :          history under deposits.  However, there is really no benefit
     223             :          for the exchange to lie here, so not checking is probably OK
     224             :          (an auditor ought to check, though). Then again, we similarly
     225             :          had no reason to check the merchant's signature (other than a
     226             :          well-formendess check). */
     227             :       /* TODO: check that deposit fee and coin value match
     228             :          our expectations from /keys! */
     229           0 :       add = GNUNET_NO;
     230             :     }
     231           1 :     else if (0 == strcasecmp (type,
     232             :                               "PAYBACK"))
     233             :     {
     234             :       struct TALER_PaybackConfirmationPS pc;
     235             :       struct TALER_ExchangePublicKeyP exchange_pub;
     236             :       struct TALER_ExchangeSignatureP exchange_sig;
     237           1 :       struct GNUNET_JSON_Specification spec[] = {
     238             :         GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     239             :                                      &exchange_sig),
     240             :         GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     241             :                                      &exchange_pub),
     242             :         GNUNET_JSON_spec_fixed_auto ("reserve_pub",
     243             :                                      &pc.reserve_pub),
     244             :         GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
     245             :                                             &pc.timestamp),
     246             :         GNUNET_JSON_spec_end()
     247             :       };
     248             : 
     249           1 :       if (GNUNET_OK !=
     250           1 :           GNUNET_JSON_parse (transaction,
     251             :                              spec,
     252             :                              NULL, NULL))
     253             :       {
     254           0 :         GNUNET_break_op (0);
     255           0 :         return GNUNET_SYSERR;
     256             :       }
     257           1 :       pc.purpose.size = htonl (sizeof (pc));
     258           1 :       pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
     259           1 :       pc.coin_pub = *coin_pub;
     260           1 :       TALER_amount_hton (&pc.payback_amount,
     261             :                          &amount);
     262           1 :       if (GNUNET_OK !=
     263           1 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK,
     264             :                                       &pc.purpose,
     265             :                                       &exchange_sig.eddsa_signature,
     266             :                                       &exchange_pub.eddsa_pub))
     267             :       {
     268           0 :         GNUNET_break_op (0);
     269           0 :         return GNUNET_SYSERR;
     270             :       }
     271           1 :       add = GNUNET_YES;
     272             :     }
     273             :     else
     274             :     {
     275             :       /* signature not supported, new version on server? */
     276           0 :       GNUNET_break_op (0);
     277           0 :       return GNUNET_SYSERR;
     278             :     }
     279           7 :     if (GNUNET_YES == add)
     280             :     {
     281             :       /* This amount should be added to the total */
     282           7 :       if (GNUNET_OK !=
     283           7 :           TALER_amount_add (total,
     284             :                             total,
     285             :                             &amount))
     286             :       {
     287             :         /* overflow in history already!? inconceivable! Bad exchange! */
     288           0 :         GNUNET_break_op (0);
     289           0 :         return GNUNET_SYSERR;
     290             :       }
     291             :     }
     292             :     else
     293             :     {
     294             :       /* This amount should be subtracted from the total.
     295             : 
     296             :          However, for the implementation, we first *add* up all of
     297             :          these negative amounts, as we might get refunds before
     298             :          deposits from a semi-evil exchange.  Then, at the end, we do
     299             :          the subtraction by calculating "total = total - rtotal" */
     300           0 :       GNUNET_assert (GNUNET_NO == add);
     301           0 :       if (GNUNET_OK !=
     302           0 :           TALER_amount_add (&rtotal,
     303             :                             &rtotal,
     304             :                             &amount))
     305             :       {
     306             :         /* overflow in refund history? inconceivable! Bad exchange! */
     307           0 :         GNUNET_break_op (0);
     308           0 :         return GNUNET_SYSERR;
     309             :       }
     310             :     }
     311             :   }
     312             : 
     313             :   /* Finally, subtract 'rtotal' from total to handle the subtractions */
     314           5 :   if (GNUNET_OK !=
     315           5 :       TALER_amount_subtract (total,
     316             :                              total,
     317             :                              &rtotal))
     318             :   {
     319             :     /* underflow in history? inconceivable! Bad exchange! */
     320           0 :     GNUNET_break_op (0);
     321           0 :     return GNUNET_SYSERR;
     322             :   }
     323             : 
     324           5 :   return GNUNET_OK;
     325             : }
     326             : 
     327             : 
     328             : /* end of exchange_api_common.c */

Generated by: LCOV version 1.13