LCOV - code coverage report
Current view: top level - lib - exchange_api_common.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 258 403 64.0 %
Date: 2021-08-30 06:43:37 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2015-2020 Taler Systems SA
       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 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             :  * Parse history given in JSON format and return it in binary
      31             :  * format.
      32             :  *
      33             :  * @param exchange connection to the exchange we can use
      34             :  * @param history JSON array with the history
      35             :  * @param reserve_pub public key of the reserve to inspect
      36             :  * @param currency currency we expect the balance to be in
      37             :  * @param[out] balance final balance
      38             :  * @param history_length number of entries in @a history
      39             :  * @param[out] rhistory array of length @a history_length, set to the
      40             :  *             parsed history entries
      41             :  * @return #GNUNET_OK if history was valid and @a rhistory and @a balance
      42             :  *         were set,
      43             :  *         #GNUNET_SYSERR if there was a protocol violation in @a history
      44             :  */
      45             : int
      46          11 : TALER_EXCHANGE_parse_reserve_history (
      47             :   struct TALER_EXCHANGE_Handle *exchange,
      48             :   const json_t *history,
      49             :   const struct TALER_ReservePublicKeyP *reserve_pub,
      50             :   const char *currency,
      51             :   struct TALER_Amount *balance,
      52             :   unsigned int history_length,
      53             :   struct TALER_EXCHANGE_ReserveHistory *rhistory)
      54          11 : {
      55          11 :   struct GNUNET_HashCode uuid[history_length];
      56             :   unsigned int uuid_off;
      57             :   struct TALER_Amount total_in;
      58             :   struct TALER_Amount total_out;
      59             : 
      60          11 :   GNUNET_assert (GNUNET_OK ==
      61             :                  TALER_amount_set_zero (currency,
      62             :                                         &total_in));
      63          11 :   GNUNET_assert (GNUNET_OK ==
      64             :                  TALER_amount_set_zero (currency,
      65             :                                         &total_out));
      66          11 :   uuid_off = 0;
      67          45 :   for (unsigned int off = 0; off<history_length; off++)
      68             :   {
      69          34 :     struct TALER_EXCHANGE_ReserveHistory *rh = &rhistory[off];
      70             :     json_t *transaction;
      71             :     struct TALER_Amount amount;
      72             :     const char *type;
      73             :     struct GNUNET_JSON_Specification hist_spec[] = {
      74          34 :       GNUNET_JSON_spec_string ("type",
      75             :                                &type),
      76          34 :       TALER_JSON_spec_amount_any ("amount",
      77             :                                   &amount),
      78             :       /* 'wire' and 'signature' are optional depending on 'type'! */
      79          34 :       GNUNET_JSON_spec_end ()
      80             :     };
      81             : 
      82          34 :     transaction = json_array_get (history,
      83             :                                   off);
      84          34 :     if (GNUNET_OK !=
      85          34 :         GNUNET_JSON_parse (transaction,
      86             :                            hist_spec,
      87             :                            NULL, NULL))
      88             :     {
      89           0 :       GNUNET_break_op (0);
      90           0 :       return GNUNET_SYSERR;
      91             :     }
      92          34 :     rhistory[off].amount = amount;
      93          34 :     if (GNUNET_YES !=
      94          34 :         TALER_amount_cmp_currency (&amount,
      95             :                                    &total_in))
      96             :     {
      97           0 :       GNUNET_break_op (0);
      98           0 :       return GNUNET_SYSERR;
      99             :     }
     100          34 :     if (0 == strcasecmp (type,
     101             :                          "CREDIT"))
     102             :     {
     103             :       const char *wire_url;
     104             :       uint64_t wire_reference;
     105             :       struct GNUNET_TIME_Absolute timestamp;
     106             :       struct GNUNET_JSON_Specification withdraw_spec[] = {
     107          11 :         GNUNET_JSON_spec_uint64 ("wire_reference",
     108             :                                  &wire_reference),
     109          11 :         TALER_JSON_spec_absolute_time ("timestamp",
     110             :                                        &timestamp),
     111          11 :         GNUNET_JSON_spec_string ("sender_account_url",
     112             :                                  &wire_url),
     113          11 :         GNUNET_JSON_spec_end ()
     114             :       };
     115             : 
     116          11 :       rh->type = TALER_EXCHANGE_RTT_CREDIT;
     117          11 :       if (0 >
     118          11 :           TALER_amount_add (&total_in,
     119             :                             &total_in,
     120             :                             &amount))
     121             :       {
     122             :         /* overflow in history already!? inconceivable! Bad exchange! */
     123           0 :         GNUNET_break_op (0);
     124           0 :         return GNUNET_SYSERR;
     125             :       }
     126          11 :       if (GNUNET_OK !=
     127          11 :           GNUNET_JSON_parse (transaction,
     128             :                              withdraw_spec,
     129             :                              NULL, NULL))
     130             :       {
     131           0 :         GNUNET_break_op (0);
     132           0 :         return GNUNET_SYSERR;
     133             :       }
     134          11 :       rh->details.in_details.sender_url = GNUNET_strdup (wire_url);
     135          11 :       rh->details.in_details.wire_reference = wire_reference;
     136          11 :       rh->details.in_details.timestamp = timestamp;
     137             :       /* end type==DEPOSIT */
     138             :     }
     139          23 :     else if (0 == strcasecmp (type,
     140             :                               "WITHDRAW"))
     141             :     {
     142             :       struct TALER_ReserveSignatureP sig;
     143             :       struct TALER_WithdrawRequestPS withdraw_purpose;
     144             :       struct TALER_Amount withdraw_fee;
     145             :       struct GNUNET_JSON_Specification withdraw_spec[] = {
     146          14 :         GNUNET_JSON_spec_fixed_auto ("reserve_sig",
     147             :                                      &sig),
     148          14 :         TALER_JSON_spec_amount_any ("withdraw_fee",
     149             :                                     &withdraw_fee),
     150          14 :         GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
     151             :                                      &withdraw_purpose.h_denomination_pub),
     152          14 :         GNUNET_JSON_spec_fixed_auto ("h_coin_envelope",
     153             :                                      &withdraw_purpose.h_coin_envelope),
     154          14 :         GNUNET_JSON_spec_end ()
     155             :       };
     156             : 
     157          14 :       rh->type = TALER_EXCHANGE_RTT_WITHDRAWAL;
     158          14 :       if (GNUNET_OK !=
     159          14 :           GNUNET_JSON_parse (transaction,
     160             :                              withdraw_spec,
     161             :                              NULL, NULL))
     162             :       {
     163           0 :         GNUNET_break_op (0);
     164           0 :         return GNUNET_SYSERR;
     165             :       }
     166             :       withdraw_purpose.purpose.size
     167          14 :         = htonl (sizeof (withdraw_purpose));
     168             :       withdraw_purpose.purpose.purpose
     169          14 :         = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
     170          14 :       withdraw_purpose.reserve_pub = *reserve_pub;
     171          14 :       TALER_amount_hton (&withdraw_purpose.amount_with_fee,
     172             :                          &amount);
     173             :       /* Check that the signature is a valid withdraw request */
     174          14 :       if (GNUNET_OK !=
     175          14 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
     176             :                                       &withdraw_purpose,
     177             :                                       &sig.eddsa_signature,
     178             :                                       &reserve_pub->eddsa_pub))
     179             :       {
     180           0 :         GNUNET_break_op (0);
     181           0 :         GNUNET_JSON_parse_free (withdraw_spec);
     182           0 :         return GNUNET_SYSERR;
     183             :       }
     184             :       /* check that withdraw fee matches expectations! */
     185             :       {
     186             :         const struct TALER_EXCHANGE_Keys *key_state;
     187             :         const struct TALER_EXCHANGE_DenomPublicKey *dki;
     188             : 
     189          14 :         key_state = TALER_EXCHANGE_get_keys (exchange);
     190          14 :         dki = TALER_EXCHANGE_get_denomination_key_by_hash (key_state,
     191             :                                                            &withdraw_purpose.
     192             :                                                            h_denomination_pub);
     193          14 :         if ( (GNUNET_YES !=
     194          14 :               TALER_amount_cmp_currency (&withdraw_fee,
     195          14 :                                          &dki->fee_withdraw)) ||
     196             :              (0 !=
     197          14 :               TALER_amount_cmp (&withdraw_fee,
     198             :                                 &dki->fee_withdraw)) )
     199             :         {
     200           0 :           GNUNET_break_op (0);
     201           0 :           GNUNET_JSON_parse_free (withdraw_spec);
     202           0 :           return GNUNET_SYSERR;
     203             :         }
     204          14 :         rh->details.withdraw.fee = withdraw_fee;
     205             :       }
     206             :       rh->details.withdraw.out_authorization_sig
     207          14 :         = json_object_get (transaction,
     208             :                            "signature");
     209             :       /* Check check that the same withdraw transaction
     210             :          isn't listed twice by the exchange. We use the
     211             :          "uuid" array to remember the hashes of all
     212             :          purposes, and compare the hashes to find
     213             :          duplicates. *///
     214          14 :       GNUNET_CRYPTO_hash (&withdraw_purpose,
     215          14 :                           ntohl (withdraw_purpose.purpose.size),
     216             :                           &uuid[uuid_off]);
     217          24 :       for (unsigned int i = 0; i<uuid_off; i++)
     218             :       {
     219          10 :         if (0 == GNUNET_memcmp (&uuid[uuid_off],
     220             :                                 &uuid[i]))
     221             :         {
     222           0 :           GNUNET_break_op (0);
     223           0 :           GNUNET_JSON_parse_free (withdraw_spec);
     224           0 :           return GNUNET_SYSERR;
     225             :         }
     226             :       }
     227          14 :       uuid_off++;
     228             : 
     229          14 :       if (0 >
     230          14 :           TALER_amount_add (&total_out,
     231             :                             &total_out,
     232             :                             &amount))
     233             :       {
     234             :         /* overflow in history already!? inconceivable! Bad exchange! */
     235           0 :         GNUNET_break_op (0);
     236           0 :         GNUNET_JSON_parse_free (withdraw_spec);
     237           0 :         return GNUNET_SYSERR;
     238             :       }
     239             :       /* end type==WITHDRAW */
     240             :     }
     241           9 :     else if (0 == strcasecmp (type,
     242             :                               "RECOUP"))
     243             :     {
     244             :       struct TALER_RecoupConfirmationPS pc;
     245             :       struct GNUNET_TIME_Absolute timestamp;
     246             :       const struct TALER_EXCHANGE_Keys *key_state;
     247             :       struct GNUNET_JSON_Specification recoup_spec[] = {
     248           4 :         GNUNET_JSON_spec_fixed_auto ("coin_pub",
     249             :                                      &pc.coin_pub),
     250           4 :         GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     251             :                                      &rh->details.recoup_details.exchange_sig),
     252           4 :         GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     253             :                                      &rh->details.recoup_details.exchange_pub),
     254           4 :         TALER_JSON_spec_absolute_time_nbo ("timestamp",
     255             :                                            &pc.timestamp),
     256           4 :         GNUNET_JSON_spec_end ()
     257             :       };
     258             : 
     259           4 :       rh->type = TALER_EXCHANGE_RTT_RECOUP;
     260           4 :       rh->amount = amount;
     261           4 :       if (GNUNET_OK !=
     262           4 :           GNUNET_JSON_parse (transaction,
     263             :                              recoup_spec,
     264             :                              NULL, NULL))
     265             :       {
     266           0 :         GNUNET_break_op (0);
     267           0 :         return GNUNET_SYSERR;
     268             :       }
     269           4 :       rh->details.recoup_details.coin_pub = pc.coin_pub;
     270           4 :       TALER_amount_hton (&pc.recoup_amount,
     271             :                          &amount);
     272           4 :       pc.purpose.size = htonl (sizeof (pc));
     273           4 :       pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP);
     274           4 :       pc.reserve_pub = *reserve_pub;
     275           4 :       timestamp = GNUNET_TIME_absolute_ntoh (pc.timestamp);
     276           4 :       rh->details.recoup_details.timestamp = timestamp;
     277             : 
     278           4 :       key_state = TALER_EXCHANGE_get_keys (exchange);
     279           4 :       if (GNUNET_OK !=
     280           4 :           TALER_EXCHANGE_test_signing_key (key_state,
     281           4 :                                            &rh->details.
     282             :                                            recoup_details.exchange_pub))
     283             :       {
     284           0 :         GNUNET_break_op (0);
     285           0 :         return GNUNET_SYSERR;
     286             :       }
     287           4 :       if (GNUNET_OK !=
     288           4 :           GNUNET_CRYPTO_eddsa_verify (
     289             :             TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP,
     290             :             &pc,
     291             :             &rh->details.recoup_details.exchange_sig.eddsa_signature,
     292             :             &rh->details.recoup_details.exchange_pub.eddsa_pub))
     293             :       {
     294           0 :         GNUNET_break_op (0);
     295           0 :         return GNUNET_SYSERR;
     296             :       }
     297           4 :       if (0 >
     298           4 :           TALER_amount_add (&total_in,
     299             :                             &total_in,
     300           4 :                             &rh->amount))
     301             :       {
     302             :         /* overflow in history already!? inconceivable! Bad exchange! */
     303           0 :         GNUNET_break_op (0);
     304           0 :         return GNUNET_SYSERR;
     305             :       }
     306             :       /* end type==RECOUP */
     307             :     }
     308           5 :     else if (0 == strcasecmp (type,
     309             :                               "CLOSING"))
     310             :     {
     311             :       const struct TALER_EXCHANGE_Keys *key_state;
     312             :       struct TALER_ReserveCloseConfirmationPS rcc;
     313             :       struct GNUNET_TIME_Absolute timestamp;
     314             :       struct GNUNET_JSON_Specification closing_spec[] = {
     315           5 :         GNUNET_JSON_spec_string (
     316             :           "receiver_account_details",
     317             :           &rh->details.close_details.receiver_account_details),
     318           5 :         GNUNET_JSON_spec_fixed_auto ("wtid",
     319             :                                      &rh->details.close_details.wtid),
     320           5 :         GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     321             :                                      &rh->details.close_details.exchange_sig),
     322           5 :         GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     323             :                                      &rh->details.close_details.exchange_pub),
     324           5 :         TALER_JSON_spec_amount_any_nbo ("closing_fee",
     325             :                                         &rcc.closing_fee),
     326           5 :         TALER_JSON_spec_absolute_time_nbo ("timestamp",
     327             :                                            &rcc.timestamp),
     328           5 :         GNUNET_JSON_spec_end ()
     329             :       };
     330             : 
     331           5 :       rh->type = TALER_EXCHANGE_RTT_CLOSE;
     332           5 :       rh->amount = amount;
     333           5 :       if (GNUNET_OK !=
     334           5 :           GNUNET_JSON_parse (transaction,
     335             :                              closing_spec,
     336             :                              NULL, NULL))
     337             :       {
     338           0 :         GNUNET_break_op (0);
     339           0 :         return GNUNET_SYSERR;
     340             :       }
     341           5 :       TALER_amount_hton (&rcc.closing_amount,
     342             :                          &amount);
     343           5 :       GNUNET_CRYPTO_hash (
     344           5 :         rh->details.close_details.receiver_account_details,
     345           5 :         strlen (rh->details.close_details.receiver_account_details) + 1,
     346             :         &rcc.h_wire);
     347           5 :       rcc.wtid = rh->details.close_details.wtid;
     348           5 :       rcc.purpose.size = htonl (sizeof (rcc));
     349           5 :       rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED);
     350           5 :       rcc.reserve_pub = *reserve_pub;
     351           5 :       timestamp = GNUNET_TIME_absolute_ntoh (rcc.timestamp);
     352           5 :       rh->details.close_details.timestamp = timestamp;
     353           5 :       TALER_amount_ntoh (&rh->details.close_details.fee,
     354             :                          &rcc.closing_fee);
     355           5 :       key_state = TALER_EXCHANGE_get_keys (exchange);
     356           5 :       if (GNUNET_OK !=
     357           5 :           TALER_EXCHANGE_test_signing_key (key_state,
     358           5 :                                            &rh->details.close_details.
     359             :                                            exchange_pub))
     360             :       {
     361           0 :         GNUNET_break_op (0);
     362           0 :         return GNUNET_SYSERR;
     363             :       }
     364           5 :       if (GNUNET_OK !=
     365           5 :           GNUNET_CRYPTO_eddsa_verify (
     366             :             TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED,
     367             :             &rcc,
     368             :             &rh->details.close_details.exchange_sig.eddsa_signature,
     369             :             &rh->details.close_details.exchange_pub.eddsa_pub))
     370             :       {
     371           0 :         GNUNET_break_op (0);
     372           0 :         return GNUNET_SYSERR;
     373             :       }
     374           5 :       if (0 >
     375           5 :           TALER_amount_add (&total_out,
     376             :                             &total_out,
     377           5 :                             &rh->amount))
     378             :       {
     379             :         /* overflow in history already!? inconceivable! Bad exchange! */
     380           0 :         GNUNET_break_op (0);
     381           0 :         return GNUNET_SYSERR;
     382             :       }
     383             :       /* end type==CLOSING */
     384             :     }
     385             :     else
     386             :     {
     387             :       /* unexpected 'type', protocol incompatibility, complain! */
     388           0 :       GNUNET_break_op (0);
     389           0 :       return GNUNET_SYSERR;
     390             :     }
     391             :   }
     392             : 
     393             :   /* check balance = total_in - total_out < withdraw-amount */
     394          11 :   if (0 >
     395          11 :       TALER_amount_subtract (balance,
     396             :                              &total_in,
     397             :                              &total_out))
     398             :   {
     399             :     /* total_in < total_out, why did the exchange ever allow this!? */
     400           0 :     GNUNET_break_op (0);
     401           0 :     return GNUNET_SYSERR;
     402             :   }
     403          11 :   return GNUNET_OK;
     404             : }
     405             : 
     406             : 
     407             : /**
     408             :  * Free memory (potentially) allocated by #TALER_EXCHANGE_parse_reserve_history().
     409             :  *
     410             :  * @param rhistory result to free
     411             :  * @param len number of entries in @a rhistory
     412             :  */
     413             : void
     414          11 : TALER_EXCHANGE_free_reserve_history (
     415             :   struct TALER_EXCHANGE_ReserveHistory *rhistory,
     416             :   unsigned int len)
     417             : {
     418          45 :   for (unsigned int i = 0; i<len; i++)
     419             :   {
     420          34 :     switch (rhistory[i].type)
     421             :     {
     422          11 :     case TALER_EXCHANGE_RTT_CREDIT:
     423          11 :       GNUNET_free (rhistory[i].details.in_details.sender_url);
     424          11 :       break;
     425          14 :     case TALER_EXCHANGE_RTT_WITHDRAWAL:
     426          14 :       break;
     427           4 :     case TALER_EXCHANGE_RTT_RECOUP:
     428           4 :       break;
     429           5 :     case TALER_EXCHANGE_RTT_CLOSE:
     430           5 :       break;
     431             :     }
     432          34 :   }
     433          11 :   GNUNET_free (rhistory);
     434          11 : }
     435             : 
     436             : 
     437             : /**
     438             :  * Verify a coins transaction history as returned by the exchange.
     439             :  *
     440             :  * @param dk fee structure for the coin, NULL to skip verifying fees
     441             :  * @param currency expected currency for the coin
     442             :  * @param coin_pub public key of the coin
     443             :  * @param history history of the coin in json encoding
     444             :  * @param[out] h_denom_pub set to the hash of the coin's denomination (if available)
     445             :  * @param[out] total how much of the coin has been spent according to @a history
     446             :  * @return #GNUNET_OK if @a history is valid, #GNUNET_SYSERR if not
     447             :  */
     448             : int
     449          12 : TALER_EXCHANGE_verify_coin_history (
     450             :   const struct TALER_EXCHANGE_DenomPublicKey *dk,
     451             :   const char *currency,
     452             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
     453             :   json_t *history,
     454             :   struct GNUNET_HashCode *h_denom_pub,
     455             :   struct TALER_Amount *total)
     456             : {
     457             :   size_t len;
     458             :   struct TALER_Amount rtotal;
     459             :   struct TALER_Amount fee;
     460             : 
     461          12 :   if (NULL == history)
     462             :   {
     463           0 :     GNUNET_break_op (0);
     464           0 :     return GNUNET_SYSERR;
     465             :   }
     466          12 :   len = json_array_size (history);
     467          12 :   if (0 == len)
     468             :   {
     469           0 :     GNUNET_break_op (0);
     470           0 :     return GNUNET_SYSERR;
     471             :   }
     472          12 :   GNUNET_assert (GNUNET_OK ==
     473             :                  TALER_amount_set_zero (currency,
     474             :                                         total));
     475          12 :   GNUNET_assert (GNUNET_OK ==
     476             :                  TALER_amount_set_zero (currency,
     477             :                                         &rtotal));
     478          36 :   for (size_t off = 0; off<len; off++)
     479             :   {
     480             :     int add;
     481             :     json_t *transaction;
     482             :     struct TALER_Amount amount;
     483             :     const char *type;
     484             :     struct GNUNET_JSON_Specification spec_glob[] = {
     485          24 :       TALER_JSON_spec_amount_any ("amount",
     486             :                                   &amount),
     487          24 :       GNUNET_JSON_spec_string ("type",
     488             :                                &type),
     489          24 :       GNUNET_JSON_spec_end ()
     490             :     };
     491             : 
     492          24 :     transaction = json_array_get (history,
     493             :                                   off);
     494          24 :     if (GNUNET_OK !=
     495          24 :         GNUNET_JSON_parse (transaction,
     496             :                            spec_glob,
     497             :                            NULL, NULL))
     498             :     {
     499           0 :       GNUNET_break_op (0);
     500           0 :       return GNUNET_SYSERR;
     501             :     }
     502          24 :     if (GNUNET_YES !=
     503          24 :         TALER_amount_cmp_currency (&amount,
     504             :                                    &rtotal))
     505             :     {
     506           0 :       GNUNET_break_op (0);
     507           0 :       return GNUNET_SYSERR;
     508             :     }
     509          24 :     add = GNUNET_SYSERR;
     510          24 :     if (0 == strcasecmp (type,
     511             :                          "DEPOSIT"))
     512             :     {
     513          11 :       struct TALER_DepositRequestPS dr = {
     514          11 :         .purpose.size = htonl (sizeof (dr)),
     515          11 :         .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT),
     516             :         .coin_pub = *coin_pub
     517             :       };
     518             :       struct TALER_CoinSpendSignatureP sig;
     519             :       struct GNUNET_JSON_Specification spec[] = {
     520          11 :         GNUNET_JSON_spec_fixed_auto ("coin_sig",
     521             :                                      &sig),
     522          11 :         GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
     523             :                                      &dr.h_contract_terms),
     524          11 :         GNUNET_JSON_spec_fixed_auto ("h_wire",
     525             :                                      &dr.h_wire),
     526          11 :         GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
     527             :                                      &dr.h_denom_pub),
     528          11 :         TALER_JSON_spec_absolute_time_nbo ("timestamp",
     529             :                                            &dr.wallet_timestamp),
     530          11 :         GNUNET_JSON_spec_mark_optional (
     531             :           TALER_JSON_spec_absolute_time_nbo ("refund_deadline",
     532             :                                              &dr.refund_deadline)),
     533          11 :         TALER_JSON_spec_amount_any_nbo ("deposit_fee",
     534             :                                         &dr.deposit_fee),
     535          11 :         GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     536             :                                      &dr.merchant),
     537          11 :         GNUNET_JSON_spec_end ()
     538             :       };
     539             : 
     540          11 :       if (GNUNET_OK !=
     541          11 :           GNUNET_JSON_parse (transaction,
     542             :                              spec,
     543             :                              NULL, NULL))
     544             :       {
     545           0 :         GNUNET_break_op (0);
     546           0 :         return GNUNET_SYSERR;
     547             :       }
     548          11 :       TALER_amount_hton (&dr.amount_with_fee,
     549             :                          &amount);
     550          11 :       if (GNUNET_OK !=
     551          11 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT,
     552             :                                       &dr,
     553             :                                       &sig.eddsa_signature,
     554             :                                       &coin_pub->eddsa_pub))
     555             :       {
     556           0 :         GNUNET_break_op (0);
     557           0 :         return GNUNET_SYSERR;
     558             :       }
     559          11 :       *h_denom_pub = dr.h_denom_pub;
     560          11 :       if (NULL != dk)
     561             :       {
     562             :         /* check that deposit fee matches our expectations from /keys! */
     563          11 :         TALER_amount_ntoh (&fee,
     564             :                            &dr.deposit_fee);
     565          11 :         if ( (GNUNET_YES !=
     566          11 :               TALER_amount_cmp_currency (&fee,
     567          11 :                                          &dk->fee_deposit)) ||
     568             :              (0 !=
     569          11 :               TALER_amount_cmp (&fee,
     570             :                                 &dk->fee_deposit)) )
     571             :         {
     572           0 :           GNUNET_break_op (0);
     573           0 :           return GNUNET_SYSERR;
     574             :         }
     575             :       }
     576          11 :       add = GNUNET_YES;
     577             :     }
     578          13 :     else if (0 == strcasecmp (type,
     579             :                               "MELT"))
     580             :     {
     581             :       struct TALER_RefreshMeltCoinAffirmationPS rm;
     582             :       struct TALER_CoinSpendSignatureP sig;
     583             :       struct GNUNET_JSON_Specification spec[] = {
     584          11 :         GNUNET_JSON_spec_fixed_auto ("coin_sig",
     585             :                                      &sig),
     586          11 :         GNUNET_JSON_spec_fixed_auto ("rc",
     587             :                                      &rm.rc),
     588          11 :         GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
     589             :                                      &rm.h_denom_pub),
     590          11 :         TALER_JSON_spec_amount_any_nbo ("melt_fee",
     591             :                                         &rm.melt_fee),
     592          11 :         GNUNET_JSON_spec_end ()
     593             :       };
     594             : 
     595          11 :       if (GNUNET_OK !=
     596          11 :           GNUNET_JSON_parse (transaction,
     597             :                              spec,
     598             :                              NULL, NULL))
     599             :       {
     600           0 :         GNUNET_break_op (0);
     601           0 :         return GNUNET_SYSERR;
     602             :       }
     603          11 :       rm.purpose.size = htonl (sizeof (rm));
     604          11 :       rm.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
     605          11 :       TALER_amount_hton (&rm.amount_with_fee,
     606             :                          &amount);
     607          11 :       rm.coin_pub = *coin_pub;
     608          11 :       if (GNUNET_OK !=
     609          11 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
     610             :                                       &rm,
     611             :                                       &sig.eddsa_signature,
     612             :                                       &coin_pub->eddsa_pub))
     613             :       {
     614           0 :         GNUNET_break_op (0);
     615           0 :         return GNUNET_SYSERR;
     616             :       }
     617          11 :       *h_denom_pub = rm.h_denom_pub;
     618          11 :       if (NULL != dk)
     619             :       {
     620             :         /* check that melt fee matches our expectations from /keys! */
     621          11 :         TALER_amount_ntoh (&fee,
     622             :                            &rm.melt_fee);
     623          11 :         if ( (GNUNET_YES !=
     624          11 :               TALER_amount_cmp_currency (&fee,
     625          11 :                                          &dk->fee_refresh)) ||
     626             :              (0 !=
     627          11 :               TALER_amount_cmp (&fee,
     628             :                                 &dk->fee_refresh)) )
     629             :         {
     630           0 :           GNUNET_break_op (0);
     631           0 :           return GNUNET_SYSERR;
     632             :         }
     633             :       }
     634          11 :       add = GNUNET_YES;
     635             :     }
     636           2 :     else if (0 == strcasecmp (type,
     637             :                               "REFUND"))
     638             :     {
     639             :       struct TALER_MerchantSignatureP sig;
     640             :       struct TALER_Amount refund_fee;
     641             :       struct TALER_Amount sig_amount;
     642           1 :       struct TALER_RefundRequestPS rr = {
     643           1 :         .purpose.size = htonl (sizeof (rr)),
     644           1 :         .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
     645             :         .coin_pub = *coin_pub
     646             :       };
     647             :       struct GNUNET_JSON_Specification spec[] = {
     648           1 :         TALER_JSON_spec_amount_any ("refund_fee",
     649             :                                     &refund_fee),
     650           1 :         GNUNET_JSON_spec_fixed_auto ("merchant_sig",
     651             :                                      &sig),
     652           1 :         GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
     653             :                                      &rr.h_contract_terms),
     654           1 :         GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     655             :                                      &rr.merchant),
     656           1 :         GNUNET_JSON_spec_uint64 ("rtransaction_id",
     657             :                                  &rr.rtransaction_id),
     658           1 :         GNUNET_JSON_spec_end ()
     659             :       };
     660             : 
     661           1 :       if (GNUNET_OK !=
     662           1 :           GNUNET_JSON_parse (transaction,
     663             :                              spec,
     664             :                              NULL, NULL))
     665             :       {
     666           0 :         GNUNET_break_op (0);
     667           0 :         return GNUNET_SYSERR;
     668             :       }
     669           1 :       if (0 >
     670           1 :           TALER_amount_add (&sig_amount,
     671             :                             &refund_fee,
     672             :                             &amount))
     673             :       {
     674           0 :         GNUNET_break_op (0);
     675           0 :         return GNUNET_SYSERR;
     676             :       }
     677           1 :       TALER_amount_hton (&rr.refund_amount,
     678             :                          &sig_amount);
     679           1 :       rr.rtransaction_id = GNUNET_htonll (rr.rtransaction_id);
     680           1 :       TALER_amount_hton (&rr.refund_amount,
     681             :                          &sig_amount);
     682           1 :       if (GNUNET_OK !=
     683           1 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
     684             :                                       &rr,
     685             :                                       &sig.eddsa_sig,
     686             :                                       &rr.merchant.eddsa_pub))
     687             :       {
     688           0 :         GNUNET_break_op (0);
     689           0 :         return GNUNET_SYSERR;
     690             :       }
     691             :       /* NOTE: theoretically, we could also check that the given
     692             :          merchant_pub and h_contract_terms appear in the
     693             :          history under deposits.  However, there is really no benefit
     694             :          for the exchange to lie here, so not checking is probably OK
     695             :          (an auditor ought to check, though). Then again, we similarly
     696             :          had no reason to check the merchant's signature (other than a
     697             :          well-formendess check). *///
     698             : 
     699             :       /* check that refund fee matches our expectations from /keys! */
     700           1 :       if (NULL != dk)
     701             :       {
     702           1 :         if ( (GNUNET_YES !=
     703           1 :               TALER_amount_cmp_currency (&refund_fee,
     704           1 :                                          &dk->fee_refund)) ||
     705             :              (0 !=
     706           1 :               TALER_amount_cmp (&refund_fee,
     707             :                                 &dk->fee_refund)) )
     708             :         {
     709           0 :           GNUNET_break_op (0);
     710           0 :           return GNUNET_SYSERR;
     711             :         }
     712             :       }
     713           1 :       add = GNUNET_NO;
     714             :     }
     715           1 :     else if (0 == strcasecmp (type,
     716             :                               "RECOUP"))
     717             :     {
     718           0 :       struct TALER_RecoupConfirmationPS pc = {
     719           0 :         .purpose.size = htonl (sizeof (pc)),
     720           0 :         .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP),
     721             :         .coin_pub = *coin_pub
     722             :       };
     723           0 :       struct TALER_RecoupRequestPS rr = {
     724           0 :         .purpose.size = htonl (sizeof (pc)),
     725           0 :         .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
     726             :         .coin_pub = *coin_pub
     727             :       };
     728             :       struct TALER_ExchangePublicKeyP exchange_pub;
     729             :       struct TALER_ExchangeSignatureP exchange_sig;
     730             :       struct TALER_CoinSpendSignatureP coin_sig;
     731             :       struct GNUNET_JSON_Specification spec[] = {
     732           0 :         TALER_JSON_spec_amount_any_nbo ("amount",
     733             :                                         &pc.recoup_amount),
     734           0 :         GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     735             :                                      &exchange_sig),
     736           0 :         GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     737             :                                      &exchange_pub),
     738           0 :         GNUNET_JSON_spec_fixed_auto ("reserve_pub",
     739             :                                      &pc.reserve_pub),
     740           0 :         GNUNET_JSON_spec_fixed_auto ("coin_sig",
     741             :                                      &coin_sig),
     742           0 :         GNUNET_JSON_spec_fixed_auto ("coin_blind",
     743             :                                      &rr.coin_blind),
     744           0 :         GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
     745             :                                      &rr.h_denom_pub),
     746           0 :         TALER_JSON_spec_absolute_time_nbo ("timestamp",
     747             :                                            &pc.timestamp),
     748           0 :         GNUNET_JSON_spec_end ()
     749             :       };
     750             : 
     751           0 :       if (GNUNET_OK !=
     752           0 :           GNUNET_JSON_parse (transaction,
     753             :                              spec,
     754             :                              NULL, NULL))
     755             :       {
     756           0 :         GNUNET_break_op (0);
     757           0 :         return GNUNET_SYSERR;
     758             :       }
     759           0 :       TALER_amount_hton (&pc.recoup_amount,
     760             :                          &amount);
     761           0 :       if (GNUNET_OK !=
     762           0 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP,
     763             :                                       &pc,
     764             :                                       &exchange_sig.eddsa_signature,
     765             :                                       &exchange_pub.eddsa_pub))
     766             :       {
     767           0 :         GNUNET_break_op (0);
     768           0 :         return GNUNET_SYSERR;
     769             :       }
     770           0 :       if (GNUNET_OK !=
     771           0 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
     772             :                                       &rr,
     773             :                                       &coin_sig.eddsa_signature,
     774             :                                       &coin_pub->eddsa_pub))
     775             :       {
     776           0 :         GNUNET_break_op (0);
     777           0 :         return GNUNET_SYSERR;
     778             :       }
     779           0 :       *h_denom_pub = rr.h_denom_pub;
     780           0 :       add = GNUNET_YES;
     781             :     }
     782           1 :     else if (0 == strcasecmp (type,
     783             :                               "RECOUP-REFRESH"))
     784             :     {
     785           0 :       struct TALER_RecoupRefreshConfirmationPS pc = {
     786           0 :         .purpose.size = htonl (sizeof (pc)),
     787           0 :         .purpose.purpose = htonl (
     788             :           TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH),
     789             :         .coin_pub = *coin_pub
     790             :       };
     791           0 :       struct TALER_RecoupRequestPS rr = {
     792           0 :         .purpose.size = htonl (sizeof (pc)),
     793           0 :         .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
     794             :         .coin_pub = *coin_pub
     795             :       };
     796             :       struct TALER_ExchangePublicKeyP exchange_pub;
     797             :       struct TALER_ExchangeSignatureP exchange_sig;
     798             :       struct TALER_CoinSpendSignatureP coin_sig;
     799             :       struct GNUNET_JSON_Specification spec[] = {
     800           0 :         TALER_JSON_spec_amount_any_nbo ("amount",
     801             :                                         &pc.recoup_amount),
     802           0 :         GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     803             :                                      &exchange_sig),
     804           0 :         GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     805             :                                      &exchange_pub),
     806           0 :         GNUNET_JSON_spec_fixed_auto ("coin_sig",
     807             :                                      &coin_sig),
     808           0 :         GNUNET_JSON_spec_fixed_auto ("old_coin_pub",
     809             :                                      &pc.old_coin_pub),
     810           0 :         GNUNET_JSON_spec_fixed_auto ("coin_blind",
     811             :                                      &rr.coin_blind),
     812           0 :         GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
     813             :                                      &rr.h_denom_pub),
     814           0 :         TALER_JSON_spec_absolute_time_nbo ("timestamp",
     815             :                                            &pc.timestamp),
     816           0 :         GNUNET_JSON_spec_end ()
     817             :       };
     818             : 
     819           0 :       if (GNUNET_OK !=
     820           0 :           GNUNET_JSON_parse (transaction,
     821             :                              spec,
     822             :                              NULL, NULL))
     823             :       {
     824           0 :         GNUNET_break_op (0);
     825           0 :         return GNUNET_SYSERR;
     826             :       }
     827           0 :       TALER_amount_hton (&pc.recoup_amount,
     828             :                          &amount);
     829           0 :       if (GNUNET_OK !=
     830           0 :           GNUNET_CRYPTO_eddsa_verify (
     831             :             TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH,
     832             :             &pc,
     833             :             &exchange_sig.eddsa_signature,
     834             :             &exchange_pub.eddsa_pub))
     835             :       {
     836           0 :         GNUNET_break_op (0);
     837           0 :         return GNUNET_SYSERR;
     838             :       }
     839           0 :       if (GNUNET_OK !=
     840           0 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
     841             :                                       &rr,
     842             :                                       &coin_sig.eddsa_signature,
     843             :                                       &coin_pub->eddsa_pub))
     844             :       {
     845           0 :         GNUNET_break_op (0);
     846           0 :         return GNUNET_SYSERR;
     847             :       }
     848           0 :       *h_denom_pub = rr.h_denom_pub;
     849           0 :       add = GNUNET_YES;
     850             :     }
     851           1 :     else if (0 == strcasecmp (type,
     852             :                               "OLD-COIN-RECOUP"))
     853             :     {
     854           1 :       struct TALER_RecoupRefreshConfirmationPS pc = {
     855           1 :         .purpose.size = htonl (sizeof (pc)),
     856           1 :         .purpose.purpose = htonl (
     857             :           TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH),
     858             :         .old_coin_pub = *coin_pub
     859             :       };
     860             :       struct TALER_ExchangePublicKeyP exchange_pub;
     861             :       struct TALER_ExchangeSignatureP exchange_sig;
     862             :       struct GNUNET_JSON_Specification spec[] = {
     863           1 :         TALER_JSON_spec_amount_any_nbo ("amount",
     864             :                                         &pc.recoup_amount),
     865           1 :         GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     866             :                                      &exchange_sig),
     867           1 :         GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     868             :                                      &exchange_pub),
     869           1 :         GNUNET_JSON_spec_fixed_auto ("coin_pub",
     870             :                                      &pc.coin_pub),
     871           1 :         TALER_JSON_spec_absolute_time_nbo ("timestamp",
     872             :                                            &pc.timestamp),
     873           1 :         GNUNET_JSON_spec_end ()
     874             :       };
     875             : 
     876           1 :       if (GNUNET_OK !=
     877           1 :           GNUNET_JSON_parse (transaction,
     878             :                              spec,
     879             :                              NULL, NULL))
     880             :       {
     881           0 :         GNUNET_break_op (0);
     882           0 :         return GNUNET_SYSERR;
     883             :       }
     884           1 :       TALER_amount_hton (&pc.recoup_amount,
     885             :                          &amount);
     886           1 :       if (GNUNET_OK !=
     887           1 :           GNUNET_CRYPTO_eddsa_verify (
     888             :             TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH,
     889             :             &pc,
     890             :             &exchange_sig.eddsa_signature,
     891             :             &exchange_pub.eddsa_pub))
     892             :       {
     893           0 :         GNUNET_break_op (0);
     894           0 :         return GNUNET_SYSERR;
     895             :       }
     896           1 :       add = GNUNET_YES;
     897             :     }
     898             :     else
     899             :     {
     900             :       /* signature not supported, new version on server? */
     901           0 :       GNUNET_break_op (0);
     902           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     903             :                   "Unexpected type `%s' in response\n",
     904             :                   type);
     905           0 :       GNUNET_assert (GNUNET_SYSERR == add);
     906           0 :       return GNUNET_SYSERR;
     907             :     }
     908             : 
     909          24 :     if (GNUNET_YES == add)
     910             :     {
     911             :       /* This amount should be added to the total */
     912          23 :       if (0 >
     913          23 :           TALER_amount_add (total,
     914             :                             total,
     915             :                             &amount))
     916             :       {
     917             :         /* overflow in history already!? inconceivable! Bad exchange! */
     918           0 :         GNUNET_break_op (0);
     919           0 :         return GNUNET_SYSERR;
     920             :       }
     921             :     }
     922             :     else
     923             :     {
     924             :       /* This amount should be subtracted from the total.
     925             : 
     926             :          However, for the implementation, we first *add* up all of
     927             :          these negative amounts, as we might get refunds before
     928             :          deposits from a semi-evil exchange.  Then, at the end, we do
     929           1 :          the subtraction by calculating "total = total - rtotal" */GNUNET_assert (GNUNET_NO == add);
     930           1 :       if (0 >
     931           1 :           TALER_amount_add (&rtotal,
     932             :                             &rtotal,
     933             :                             &amount))
     934             :       {
     935             :         /* overflow in refund history? inconceivable! Bad exchange! */
     936           0 :         GNUNET_break_op (0);
     937           0 :         return GNUNET_SYSERR;
     938             :       }
     939             : 
     940             :     }
     941             :   }
     942             : 
     943             : 
     944             :   /* Finally, subtract 'rtotal' from total to handle the subtractions */
     945          12 :   if (0 >
     946          12 :       TALER_amount_subtract (total,
     947             :                              total,
     948             :                              &rtotal))
     949             :   {
     950             :     /* underflow in history? inconceivable! Bad exchange! */
     951           0 :     GNUNET_break_op (0);
     952           0 :     return GNUNET_SYSERR;
     953             :   }
     954             : 
     955          12 :   return GNUNET_OK;
     956             : }
     957             : 
     958             : 
     959             : /**
     960             :  * Obtain meta data about an exchange (online) signing
     961             :  * key.
     962             :  *
     963             :  * @param keys from where to obtain the meta data
     964             :  * @param exchange_pub public key to lookup
     965             :  * @return NULL on error (@a exchange_pub not known)
     966             :  */
     967             : const struct TALER_EXCHANGE_SigningPublicKey *
     968           1 : TALER_EXCHANGE_get_signing_key_info (
     969             :   const struct TALER_EXCHANGE_Keys *keys,
     970             :   const struct TALER_ExchangePublicKeyP *exchange_pub)
     971             : {
     972           1 :   for (unsigned int i = 0; i<keys->num_sign_keys; i++)
     973             :   {
     974           1 :     const struct TALER_EXCHANGE_SigningPublicKey *spk
     975           1 :       = &keys->sign_keys[i];
     976             : 
     977           1 :     if (0 == GNUNET_memcmp (exchange_pub,
     978             :                             &spk->key))
     979           1 :       return spk;
     980             :   }
     981           0 :   return NULL;
     982             : }
     983             : 
     984             : 
     985             : /* end of exchange_api_common.c */

Generated by: LCOV version 1.14