LCOV - code coverage report
Current view: top level - lib - exchange_api_common.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 55.8 % 523 292
Test Date: 2026-10-29 19:05:17 Functions: 78.3 % 23 18

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2015-2023 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 "taler/taler_json_lib.h"
      23              : #include <microhttpd.h>
      24              : #include <gnunet/gnunet_curl_lib.h>
      25              : #include "exchange_api_common.h"
      26              : #include "exchange_api_handle.h"
      27              : #include "taler/taler_signatures.h"
      28              : 
      29              : 
      30              : const struct TALER_EXCHANGE_SigningPublicKey *
      31            4 : TALER_EXCHANGE_get_signing_key_info (
      32              :   const struct TALER_EXCHANGE_Keys *keys,
      33              :   const struct TALER_ExchangePublicKeyP *exchange_pub)
      34              : {
      35            4 :   for (unsigned int i = 0; i<keys->num_sign_keys; i++)
      36              :   {
      37            4 :     const struct TALER_EXCHANGE_SigningPublicKey *spk
      38            4 :       = &keys->sign_keys[i];
      39              : 
      40            4 :     if (0 == GNUNET_memcmp (exchange_pub,
      41              :                             &spk->key))
      42            4 :       return spk;
      43              :   }
      44            0 :   return NULL;
      45              : }
      46              : 
      47              : 
      48              : enum GNUNET_GenericReturnValue
      49            0 : TALER_EXCHANGE_check_purse_create_conflict_ (
      50              :   const struct TALER_PurseContractSignatureP *cpurse_sig,
      51              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
      52              :   const json_t *proof)
      53              : {
      54              :   struct TALER_Amount amount;
      55              :   uint32_t min_age;
      56              :   struct GNUNET_TIME_Timestamp purse_expiration;
      57              :   struct TALER_PurseContractSignatureP purse_sig;
      58              :   struct TALER_PrivateContractHashP h_contract_terms;
      59              :   struct TALER_PurseMergePublicKeyP merge_pub;
      60              :   struct GNUNET_JSON_Specification spec[] = {
      61            0 :     TALER_JSON_spec_amount_any ("amount",
      62              :                                 &amount),
      63            0 :     GNUNET_JSON_spec_uint32 ("min_age",
      64              :                              &min_age),
      65            0 :     GNUNET_JSON_spec_timestamp ("purse_expiration",
      66              :                                 &purse_expiration),
      67            0 :     GNUNET_JSON_spec_fixed_auto ("purse_sig",
      68              :                                  &purse_sig),
      69            0 :     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
      70              :                                  &h_contract_terms),
      71            0 :     GNUNET_JSON_spec_fixed_auto ("merge_pub",
      72              :                                  &merge_pub),
      73            0 :     GNUNET_JSON_spec_end ()
      74              :   };
      75              : 
      76            0 :   if (GNUNET_OK !=
      77            0 :       GNUNET_JSON_parse (proof,
      78              :                          spec,
      79              :                          NULL, NULL))
      80              :   {
      81            0 :     GNUNET_break_op (0);
      82            0 :     return GNUNET_SYSERR;
      83              :   }
      84            0 :   if (GNUNET_OK !=
      85            0 :       TALER_wallet_purse_create_verify (purse_expiration,
      86              :                                         &h_contract_terms,
      87              :                                         &merge_pub,
      88              :                                         min_age,
      89              :                                         &amount,
      90              :                                         purse_pub,
      91              :                                         &purse_sig))
      92              :   {
      93            0 :     GNUNET_break_op (0);
      94            0 :     return GNUNET_SYSERR;
      95              :   }
      96            0 :   if (0 ==
      97            0 :       GNUNET_memcmp (&purse_sig,
      98              :                      cpurse_sig))
      99              :   {
     100              :     /* Must be the SAME data, not a conflict! */
     101            0 :     GNUNET_break_op (0);
     102            0 :     return GNUNET_SYSERR;
     103              :   }
     104            0 :   return GNUNET_OK;
     105              : }
     106              : 
     107              : 
     108              : enum GNUNET_GenericReturnValue
     109            2 : TALER_EXCHANGE_check_purse_merge_conflict_ (
     110              :   const struct TALER_PurseMergeSignatureP *cmerge_sig,
     111              :   const struct TALER_PurseMergePublicKeyP *merge_pub,
     112              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
     113              :   const char *exchange_url,
     114              :   const json_t *proof)
     115              : {
     116              :   struct TALER_PurseMergeSignatureP merge_sig;
     117              :   struct GNUNET_TIME_Timestamp merge_timestamp;
     118            2 :   const char *partner_url = NULL;
     119              :   struct TALER_ReservePublicKeyP reserve_pub;
     120              :   struct GNUNET_JSON_Specification spec[] = {
     121            2 :     GNUNET_JSON_spec_mark_optional (
     122              :       TALER_JSON_spec_web_url ("partner_url",
     123              :                                &partner_url),
     124              :       NULL),
     125            2 :     GNUNET_JSON_spec_timestamp ("merge_timestamp",
     126              :                                 &merge_timestamp),
     127            2 :     GNUNET_JSON_spec_fixed_auto ("merge_sig",
     128              :                                  &merge_sig),
     129            2 :     GNUNET_JSON_spec_fixed_auto ("reserve_pub",
     130              :                                  &reserve_pub),
     131            2 :     GNUNET_JSON_spec_end ()
     132              :   };
     133              :   struct TALER_NormalizedPayto payto_uri;
     134              : 
     135            2 :   if (GNUNET_OK !=
     136            2 :       GNUNET_JSON_parse (proof,
     137              :                          spec,
     138              :                          NULL, NULL))
     139              :   {
     140            0 :     GNUNET_break_op (0);
     141            0 :     return GNUNET_SYSERR;
     142              :   }
     143            2 :   if (NULL == partner_url)
     144            2 :     partner_url = exchange_url;
     145            2 :   payto_uri = TALER_reserve_make_payto (partner_url,
     146              :                                         &reserve_pub);
     147            2 :   if (GNUNET_OK !=
     148            2 :       TALER_wallet_purse_merge_verify (
     149              :         payto_uri,
     150              :         merge_timestamp,
     151              :         purse_pub,
     152              :         merge_pub,
     153              :         &merge_sig))
     154              :   {
     155            0 :     GNUNET_break_op (0);
     156            0 :     GNUNET_free (payto_uri.normalized_payto);
     157            0 :     return GNUNET_SYSERR;
     158              :   }
     159            2 :   GNUNET_free (payto_uri.normalized_payto);
     160            2 :   if (0 ==
     161            2 :       GNUNET_memcmp (&merge_sig,
     162              :                      cmerge_sig))
     163              :   {
     164              :     /* Must be the SAME data, not a conflict! */
     165            0 :     GNUNET_break_op (0);
     166            0 :     return GNUNET_SYSERR;
     167              :   }
     168            2 :   return GNUNET_OK;
     169              : }
     170              : 
     171              : 
     172              : enum GNUNET_GenericReturnValue
     173            0 : TALER_EXCHANGE_check_purse_coin_conflict_ (
     174              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
     175              :   const char *exchange_url,
     176              :   const json_t *proof,
     177              :   struct TALER_DenominationHashP *h_denom_pub,
     178              :   struct TALER_AgeCommitmentHashP *phac,
     179              :   struct TALER_CoinSpendPublicKeyP *coin_pub,
     180              :   struct TALER_CoinSpendSignatureP *coin_sig)
     181              : {
     182            0 :   const char *partner_url = NULL;
     183              :   struct TALER_Amount amount;
     184              :   struct GNUNET_JSON_Specification spec[] = {
     185            0 :     GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
     186              :                                  h_denom_pub),
     187            0 :     GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
     188              :                                  phac),
     189            0 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
     190              :                                  coin_sig),
     191            0 :     GNUNET_JSON_spec_fixed_auto ("coin_pub",
     192              :                                  coin_pub),
     193            0 :     GNUNET_JSON_spec_mark_optional (
     194              :       TALER_JSON_spec_web_url ("partner_url",
     195              :                                &partner_url),
     196              :       NULL),
     197            0 :     TALER_JSON_spec_amount_any ("amount",
     198              :                                 &amount),
     199            0 :     GNUNET_JSON_spec_end ()
     200              :   };
     201              : 
     202            0 :   if (GNUNET_OK !=
     203            0 :       GNUNET_JSON_parse (proof,
     204              :                          spec,
     205              :                          NULL, NULL))
     206              :   {
     207            0 :     GNUNET_break_op (0);
     208            0 :     return GNUNET_SYSERR;
     209              :   }
     210            0 :   if (NULL == partner_url)
     211            0 :     partner_url = exchange_url;
     212            0 :   if (GNUNET_OK !=
     213            0 :       TALER_wallet_purse_deposit_verify (
     214              :         partner_url,
     215              :         purse_pub,
     216              :         &amount,
     217              :         h_denom_pub,
     218              :         phac,
     219              :         coin_pub,
     220              :         coin_sig))
     221              :   {
     222            0 :     GNUNET_break_op (0);
     223            0 :     return GNUNET_SYSERR;
     224              :   }
     225            0 :   return GNUNET_OK;
     226              : }
     227              : 
     228              : 
     229              : enum GNUNET_GenericReturnValue
     230            0 : TALER_EXCHANGE_check_purse_econtract_conflict_ (
     231              :   const struct TALER_PurseContractSignatureP *ccontract_sig,
     232              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
     233              :   const json_t *proof)
     234              : {
     235              :   struct TALER_ContractDiffiePublicP contract_pub;
     236              :   struct TALER_PurseContractSignatureP contract_sig;
     237              :   struct GNUNET_HashCode h_econtract;
     238              :   struct GNUNET_JSON_Specification spec[] = {
     239            0 :     GNUNET_JSON_spec_fixed_auto ("h_econtract",
     240              :                                  &h_econtract),
     241            0 :     GNUNET_JSON_spec_fixed_auto ("econtract_sig",
     242              :                                  &contract_sig),
     243            0 :     GNUNET_JSON_spec_fixed_auto ("contract_pub",
     244              :                                  &contract_pub),
     245            0 :     GNUNET_JSON_spec_end ()
     246              :   };
     247              : 
     248            0 :   if (GNUNET_OK !=
     249            0 :       GNUNET_JSON_parse (proof,
     250              :                          spec,
     251              :                          NULL, NULL))
     252              :   {
     253            0 :     GNUNET_break_op (0);
     254            0 :     return GNUNET_SYSERR;
     255              :   }
     256            0 :   if (GNUNET_OK !=
     257            0 :       TALER_wallet_econtract_upload_verify2 (
     258              :         &h_econtract,
     259              :         &contract_pub,
     260              :         purse_pub,
     261              :         &contract_sig))
     262              :   {
     263            0 :     GNUNET_break_op (0);
     264            0 :     return GNUNET_SYSERR;
     265              :   }
     266            0 :   if (0 ==
     267            0 :       GNUNET_memcmp (&contract_sig,
     268              :                      ccontract_sig))
     269              :   {
     270              :     /* Must be the SAME data, not a conflict! */
     271            0 :     GNUNET_break_op (0);
     272            0 :     return GNUNET_SYSERR;
     273              :   }
     274            0 :   return GNUNET_OK;
     275              : }
     276              : 
     277              : 
     278              : enum GNUNET_GenericReturnValue
     279           12 : TALER_EXCHANGE_parse_coin_denomination_conflict_ (
     280              :   const json_t *proof,
     281              :   struct TALER_EXCHANGE_CoinDenominationConflict *cdc)
     282              : {
     283              :   struct GNUNET_JSON_Specification spec[] = {
     284           12 :     GNUNET_JSON_spec_fixed_auto ("coin_pub",
     285              :                                  &cdc->coin_pub),
     286           12 :     TALER_JSON_spec_denom_pub ("prev_denom_pub",
     287              :                                &cdc->prev_denom_pub),
     288           12 :     TALER_JSON_spec_denom_sig ("prev_denom_sig",
     289              :                                &cdc->prev_denom_sig),
     290           12 :     GNUNET_JSON_spec_mark_optional (
     291           12 :       GNUNET_JSON_spec_fixed_auto ("prev_h_age_commitment",
     292              :                                    &cdc->prev_h_age_commitment),
     293              :       &cdc->no_prev_age_commitment),
     294           12 :     GNUNET_JSON_spec_end ()
     295              :   };
     296              : 
     297           12 :   memset (cdc,
     298              :           0,
     299              :           sizeof (*cdc));
     300           12 :   if (GNUNET_OK !=
     301           12 :       GNUNET_JSON_parse (proof,
     302              :                          spec,
     303              :                          NULL, NULL))
     304              :   {
     305            1 :     GNUNET_break_op (0);
     306            1 :     return GNUNET_SYSERR;
     307              :   }
     308           11 :   TALER_denom_pub_hash (&cdc->prev_denom_pub,
     309              :                         &cdc->prev_h_denom_pub);
     310           11 :   cdc->verified = false;
     311           11 :   return GNUNET_OK;
     312              : }
     313              : 
     314              : 
     315              : enum GNUNET_GenericReturnValue
     316           11 : TALER_EXCHANGE_check_coin_denomination_conflict_ (
     317              :   const struct TALER_EXCHANGE_Keys *keys,
     318              :   const struct TALER_DenominationHashP *h_denom_pub,
     319              :   struct TALER_EXCHANGE_CoinDenominationConflict *cdc)
     320              : {
     321              :   const struct TALER_EXCHANGE_DenomPublicKey *dk;
     322           11 :   struct TALER_CoinPublicInfo cpi = {
     323              :     .coin_pub = cdc->coin_pub,
     324              :     .denom_pub_hash = cdc->prev_h_denom_pub,
     325              :     .h_age_commitment = cdc->prev_h_age_commitment,
     326           11 :     .no_age_commitment = cdc->no_prev_age_commitment,
     327              :     .denom_sig = cdc->prev_denom_sig
     328              :   };
     329              : 
     330           11 :   cdc->verified = false;
     331           11 :   if (0 ==
     332           11 :       GNUNET_memcmp (h_denom_pub,
     333              :                      &cdc->prev_h_denom_pub))
     334              :   {
     335              :     /* Must be a DIFFERENT denomination, not a conflict! */
     336            1 :     GNUNET_break_op (0);
     337            1 :     return GNUNET_SYSERR;
     338              :   }
     339           10 :   dk = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
     340           10 :                                                     &cdc->prev_h_denom_pub);
     341           10 :   if (NULL == dk)
     342              :   {
     343              :     /* TODO[oec]: the exchange names a denomination that is not (or no
     344              :        longer) in our /keys, for example one that expired.  We cannot
     345              :        verify the signature, but we also cannot tell that the proof is
     346              :        wrong.  We accept the response as unverifiable and leave the
     347              :        decision to the application (see @e verified). */
     348            1 :     return GNUNET_NO;
     349              :   }
     350              :   /* the age commitment hash must be present exactly if the
     351              :      denomination is age-restricted */
     352            9 :   if (cdc->no_prev_age_commitment !=
     353            9 :       (0 == dk->key.age_mask.bits))
     354              :   {
     355            2 :     GNUNET_break_op (0);
     356            2 :     return GNUNET_SYSERR;
     357              :   }
     358            7 :   if (0 !=
     359            7 :       TALER_denom_pub_cmp (&dk->key,
     360            7 :                            &cdc->prev_denom_pub))
     361              :   {
     362              :     /* hash matches, key does not?! */
     363            0 :     GNUNET_break_op (0);
     364            0 :     return GNUNET_SYSERR;
     365              :   }
     366            7 :   if (GNUNET_OK !=
     367            7 :       TALER_test_coin_valid (&cpi,
     368            7 :                              &cdc->prev_denom_pub))
     369              :   {
     370            2 :     GNUNET_break_op (0);
     371            2 :     return GNUNET_SYSERR;
     372              :   }
     373            5 :   cdc->verified = true;
     374            5 :   return GNUNET_OK;
     375              : }
     376              : 
     377              : 
     378              : void
     379           11 : TALER_EXCHANGE_free_coin_denomination_conflict_ (
     380              :   struct TALER_EXCHANGE_CoinDenominationConflict *cdc)
     381              : {
     382           11 :   TALER_denom_pub_free (&cdc->prev_denom_pub);
     383           11 :   TALER_denom_sig_free (&cdc->prev_denom_sig);
     384           11 : }
     385              : 
     386              : 
     387              : enum GNUNET_GenericReturnValue
     388            8 : TALER_EXCHANGE_parse_coin_age_commitment_conflict_ (
     389              :   const json_t *proof,
     390              :   struct TALER_EXCHANGE_CoinAgeCommitmentConflict *cac)
     391              : {
     392              :   struct GNUNET_JSON_Specification spec[] = {
     393            8 :     GNUNET_JSON_spec_fixed_auto ("coin_pub",
     394              :                                  &cac->coin_pub),
     395            8 :     GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
     396              :                                  &cac->h_denom_pub),
     397            8 :     GNUNET_JSON_spec_mark_optional (
     398            8 :       GNUNET_JSON_spec_fixed_auto ("expected_age_commitment_hash",
     399              :                                    &cac->expected_age_commitment_hash),
     400              :       &cac->no_expected_age_commitment),
     401            8 :     GNUNET_JSON_spec_string ("conflict_detail",
     402              :                              &cac->conflict_detail),
     403            8 :     TALER_JSON_spec_denom_sig ("prev_denom_sig",
     404              :                                &cac->prev_denom_sig),
     405            8 :     GNUNET_JSON_spec_end ()
     406              :   };
     407              : 
     408            8 :   memset (cac,
     409              :           0,
     410              :           sizeof (*cac));
     411            8 :   if (GNUNET_OK !=
     412            8 :       GNUNET_JSON_parse (proof,
     413              :                          spec,
     414              :                          NULL, NULL))
     415              :   {
     416            0 :     GNUNET_break_op (0);
     417            0 :     return GNUNET_SYSERR;
     418              :   }
     419            8 :   cac->verified = false;
     420            8 :   return GNUNET_OK;
     421              : }
     422              : 
     423              : 
     424              : enum GNUNET_GenericReturnValue
     425            8 : TALER_EXCHANGE_check_coin_age_commitment_conflict_ (
     426              :   const struct TALER_EXCHANGE_Keys *keys,
     427              :   const struct TALER_DenominationHashP *h_denom_pub,
     428              :   const struct TALER_AgeCommitmentHashP *h_age_commitment,
     429              :   struct TALER_EXCHANGE_CoinAgeCommitmentConflict *cac)
     430              : {
     431              :   const struct TALER_EXCHANGE_DenomPublicKey *dk;
     432            8 :   struct TALER_CoinPublicInfo cpi = {
     433              :     .coin_pub = cac->coin_pub,
     434              :     .denom_pub_hash = cac->h_denom_pub,
     435              :     .h_age_commitment = cac->expected_age_commitment_hash,
     436            8 :     .no_age_commitment = cac->no_expected_age_commitment,
     437              :     .denom_sig = cac->prev_denom_sig
     438              :   };
     439              : 
     440            8 :   cac->verified = false;
     441            8 :   if (0 !=
     442            8 :       GNUNET_memcmp (h_denom_pub,
     443              :                      &cac->h_denom_pub))
     444              :   {
     445              :     /* an age conflict is about the SAME denomination */
     446            1 :     GNUNET_break_op (0);
     447            1 :     return GNUNET_SYSERR;
     448              :   }
     449            7 :   if (cac->no_expected_age_commitment == (NULL == h_age_commitment))
     450              :   {
     451              :     /* both absent, or both present: then they must differ */
     452            5 :     if ( (cac->no_expected_age_commitment) ||
     453              :          (0 ==
     454            4 :           GNUNET_memcmp (h_age_commitment,
     455              :                          &cac->expected_age_commitment_hash)) )
     456              :     {
     457              :       /* Must be a DIFFERENT age commitment, not a conflict! */
     458            2 :       GNUNET_break_op (0);
     459            2 :       return GNUNET_SYSERR;
     460              :     }
     461              :   }
     462            5 :   dk = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
     463            5 :                                                     &cac->h_denom_pub);
     464            5 :   if (NULL == dk)
     465              :   {
     466              :     /* TODO[oec]: as for the denomination conflict, the denomination is
     467              :        not in our /keys, so the signature cannot be checked; accept the
     468              :        response as unverifiable. */
     469            1 :     return GNUNET_NO;
     470              :   }
     471            4 :   if (GNUNET_OK !=
     472            4 :       TALER_test_coin_valid (&cpi,
     473              :                              &dk->key))
     474              :   {
     475            1 :     GNUNET_break_op (0);
     476            1 :     return GNUNET_SYSERR;
     477              :   }
     478            3 :   cac->verified = true;
     479            3 :   return GNUNET_OK;
     480              : }
     481              : 
     482              : 
     483              : void
     484            8 : TALER_EXCHANGE_free_coin_age_commitment_conflict_ (
     485              :   struct TALER_EXCHANGE_CoinAgeCommitmentConflict *cac)
     486              : {
     487            8 :   TALER_denom_sig_free (&cac->prev_denom_sig);
     488            8 :   cac->conflict_detail = NULL;
     489            8 : }
     490              : 
     491              : 
     492              : enum GNUNET_GenericReturnValue
     493            3 : TALER_EXCHANGE_check_coin_conflict_ (
     494              :   const struct TALER_EXCHANGE_Keys *keys,
     495              :   enum TALER_ErrorCode ec,
     496              :   const json_t *proof,
     497              :   TALER_EXCHANGE_CoinLookupCallback_ lookup,
     498              :   void *lookup_cls,
     499              :   struct TALER_EXCHANGE_CoinConflict *cc)
     500              : {
     501            3 :   const struct TALER_DenominationHashP *h_denom_pub = NULL;
     502            3 :   const struct TALER_AgeCommitmentHashP *h_age_commitment = NULL;
     503              : 
     504            3 :   cc->ec = TALER_EC_NONE;
     505            3 :   switch (ec)
     506              :   {
     507            3 :   case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
     508              :     {
     509            3 :       struct TALER_EXCHANGE_CoinDenominationConflict *cdc
     510              :         = &cc->details.denomination_conflict;
     511              : 
     512            3 :       if (GNUNET_OK !=
     513            3 :           TALER_EXCHANGE_parse_coin_denomination_conflict_ (proof,
     514              :                                                             cdc))
     515              :       {
     516            0 :         GNUNET_break_op (0);
     517            0 :         return GNUNET_SYSERR;
     518              :       }
     519            3 :       if ( (GNUNET_OK !=
     520            3 :             lookup (lookup_cls,
     521            3 :                     &cdc->coin_pub,
     522              :                     &h_denom_pub,
     523            3 :                     &h_age_commitment)) ||
     524              :            (GNUNET_SYSERR ==
     525            3 :             TALER_EXCHANGE_check_coin_denomination_conflict_ (keys,
     526              :                                                               h_denom_pub,
     527              :                                                               cdc)) )
     528              :       {
     529            0 :         GNUNET_break_op (0);
     530            0 :         TALER_EXCHANGE_free_coin_denomination_conflict_ (cdc);
     531            0 :         return GNUNET_SYSERR;
     532              :       }
     533            3 :       cc->ec = ec;
     534            3 :       return GNUNET_OK;
     535              :     }
     536            0 :   case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH:
     537              :     {
     538            0 :       struct TALER_EXCHANGE_CoinAgeCommitmentConflict *cac
     539              :         = &cc->details.age_commitment_conflict;
     540              : 
     541            0 :       if (GNUNET_OK !=
     542            0 :           TALER_EXCHANGE_parse_coin_age_commitment_conflict_ (proof,
     543              :                                                               cac))
     544              :       {
     545            0 :         GNUNET_break_op (0);
     546            0 :         return GNUNET_SYSERR;
     547              :       }
     548            0 :       if ( (GNUNET_OK !=
     549            0 :             lookup (lookup_cls,
     550            0 :                     &cac->coin_pub,
     551              :                     &h_denom_pub,
     552            0 :                     &h_age_commitment)) ||
     553              :            (GNUNET_SYSERR ==
     554            0 :             TALER_EXCHANGE_check_coin_age_commitment_conflict_ (
     555              :               keys,
     556              :               h_denom_pub,
     557              :               h_age_commitment,
     558              :               cac)) )
     559              :       {
     560            0 :         GNUNET_break_op (0);
     561            0 :         TALER_EXCHANGE_free_coin_age_commitment_conflict_ (cac);
     562            0 :         return GNUNET_SYSERR;
     563              :       }
     564            0 :       cc->ec = ec;
     565            0 :       return GNUNET_OK;
     566              :     }
     567            0 :   default:
     568            0 :     GNUNET_break (0);
     569            0 :     return GNUNET_SYSERR;
     570              :   }
     571              : }
     572              : 
     573              : 
     574              : void
     575          204 : TALER_EXCHANGE_free_coin_conflict_ (
     576              :   const struct TALER_EXCHANGE_HttpResponse *hr,
     577              :   struct TALER_EXCHANGE_CoinConflict *cc)
     578              : {
     579          204 :   if (MHD_HTTP_CONFLICT != hr->http_status)
     580          171 :     return;
     581           33 :   if (hr->ec != cc->ec)
     582           30 :     return;
     583            3 :   switch (cc->ec)
     584              :   {
     585            3 :   case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
     586            3 :     TALER_EXCHANGE_free_coin_denomination_conflict_ (
     587              :       &cc->details.denomination_conflict);
     588            3 :     break;
     589            0 :   case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH:
     590            0 :     TALER_EXCHANGE_free_coin_age_commitment_conflict_ (
     591              :       &cc->details.age_commitment_conflict);
     592            0 :     break;
     593            0 :   default:
     594            0 :     break;
     595              :   }
     596            3 :   cc->ec = TALER_EC_NONE;
     597              : }
     598              : 
     599              : 
     600              : enum GNUNET_GenericReturnValue
     601            0 : TALER_EXCHANGE_get_min_denomination_ (
     602              :   const struct TALER_EXCHANGE_Keys *keys,
     603              :   struct TALER_Amount *min)
     604              : {
     605            0 :   bool have_min = false;
     606            0 :   for (unsigned int i = 0; i<keys->num_denom_keys; i++)
     607              :   {
     608            0 :     const struct TALER_EXCHANGE_DenomPublicKey *dk = &keys->denom_keys[i];
     609              : 
     610            0 :     if (! have_min)
     611              :     {
     612            0 :       *min = dk->value;
     613            0 :       have_min = true;
     614            0 :       continue;
     615              :     }
     616            0 :     if (1 != TALER_amount_cmp (min,
     617              :                                &dk->value))
     618            0 :       continue;
     619            0 :     *min = dk->value;
     620              :   }
     621            0 :   if (! have_min)
     622              :   {
     623            0 :     GNUNET_break (0);
     624            0 :     return GNUNET_SYSERR;
     625              :   }
     626            0 :   return GNUNET_OK;
     627              : }
     628              : 
     629              : 
     630              : enum GNUNET_GenericReturnValue
     631          120 : TALER_EXCHANGE_verify_deposit_signature_ (
     632              :   const struct TALER_EXCHANGE_DepositContractDetail *dcd,
     633              :   const struct TALER_ExtensionPolicyHashP *ech,
     634              :   const struct TALER_MerchantWireHashP *h_wire,
     635              :   const struct TALER_EXCHANGE_CoinDepositDetail *cdd,
     636              :   const struct TALER_EXCHANGE_DenomPublicKey *dki)
     637              : {
     638          120 :   if (GNUNET_OK !=
     639          120 :       TALER_wallet_deposit_verify (&cdd->amount,
     640              :                                    &dki->fees.deposit,
     641              :                                    h_wire,
     642              :                                    &dcd->h_contract_terms,
     643              :                                    &dcd->wallet_data_hash,
     644              :                                    &cdd->h_age_commitment,
     645              :                                    ech,
     646              :                                    &cdd->h_denom_pub,
     647              :                                    dcd->wallet_timestamp,
     648              :                                    &dcd->merchant_pub,
     649              :                                    dcd->refund_deadline,
     650              :                                    &cdd->coin_pub,
     651              :                                    &cdd->coin_sig))
     652              :   {
     653            0 :     GNUNET_break_op (0);
     654            0 :     TALER_LOG_WARNING ("Invalid coin signature on /deposit request!\n");
     655            0 :     TALER_LOG_DEBUG ("... amount_with_fee was %s\n",
     656              :                      TALER_amount2s (&cdd->amount));
     657            0 :     TALER_LOG_DEBUG ("... deposit_fee was %s\n",
     658              :                      TALER_amount2s (&dki->fees.deposit));
     659            0 :     return GNUNET_SYSERR;
     660              :   }
     661              : 
     662              :   /* check coin signature */
     663              :   {
     664          120 :     struct TALER_CoinPublicInfo coin_info = {
     665              :       .coin_pub = cdd->coin_pub,
     666              :       .denom_pub_hash = cdd->h_denom_pub,
     667              :       .denom_sig = cdd->denom_sig,
     668              :       .h_age_commitment = cdd->h_age_commitment,
     669              :     };
     670              : 
     671          120 :     if (GNUNET_YES !=
     672          120 :         TALER_test_coin_valid (&coin_info,
     673              :                                &dki->key))
     674              :     {
     675            0 :       GNUNET_break_op (0);
     676            0 :       TALER_LOG_WARNING ("Invalid coin passed for /deposit\n");
     677            0 :       return GNUNET_SYSERR;
     678              :     }
     679              :   }
     680              : 
     681              :   /* Check coin does make a contribution */
     682          120 :   if (0 < TALER_amount_cmp (&dki->fees.deposit,
     683              :                             &cdd->amount))
     684              :   {
     685            0 :     GNUNET_break_op (0);
     686            0 :     TALER_LOG_WARNING ("Deposit amount smaller than fee\n");
     687            0 :     return GNUNET_SYSERR;
     688              :   }
     689          120 :   return GNUNET_OK;
     690              : }
     691              : 
     692              : 
     693              : /**
     694              :  * Parse account restriction in @a jrest into @a rest.
     695              :  *
     696              :  * @param jresta array of account restrictions in JSON
     697              :  * @param[out] resta_len set to length of @a resta
     698              :  * @param[out] resta account restriction array to set
     699              :  * @return #GNUNET_OK on success
     700              :  */
     701              : static enum GNUNET_GenericReturnValue
     702          108 : parse_restrictions (const json_t *jresta,
     703              :                     unsigned int *resta_len,
     704              :                     struct TALER_EXCHANGE_AccountRestriction **resta)
     705              : {
     706              :   size_t alen;
     707              : 
     708          108 :   if (! json_is_array (jresta))
     709              :   {
     710            0 :     GNUNET_break_op (0);
     711            0 :     return GNUNET_SYSERR;
     712              :   }
     713          108 :   alen = json_array_size (jresta);
     714          108 :   if (0 == alen)
     715              :   {
     716              :     /* no restrictions, perfectly OK */
     717          108 :     *resta = NULL;
     718          108 :     return GNUNET_OK;
     719              :   }
     720            0 :   *resta_len = (unsigned int) alen;
     721            0 :   GNUNET_assert (alen == *resta_len);
     722            0 :   *resta = GNUNET_new_array (*resta_len,
     723              :                              struct TALER_EXCHANGE_AccountRestriction);
     724            0 :   for (unsigned int i = 0; i< *resta_len; i++)
     725              :   {
     726            0 :     const json_t *jr = json_array_get (jresta,
     727              :                                        i);
     728            0 :     struct TALER_EXCHANGE_AccountRestriction *ar = &(*resta)[i];
     729            0 :     const char *type = json_string_value (json_object_get (jr,
     730              :                                                            "type"));
     731              : 
     732            0 :     if (NULL == type)
     733              :     {
     734            0 :       GNUNET_break (0);
     735            0 :       goto fail;
     736              :     }
     737            0 :     if (0 == strcmp (type,
     738              :                      "deny"))
     739              :     {
     740            0 :       ar->type = TALER_EXCHANGE_AR_DENY;
     741            0 :       continue;
     742              :     }
     743            0 :     if (0 == strcmp (type,
     744              :                      "regex"))
     745            0 :     {
     746              :       const char *regex;
     747              :       const char *hint;
     748              :       struct GNUNET_JSON_Specification spec[] = {
     749            0 :         GNUNET_JSON_spec_string (
     750              :           "payto_regex",
     751              :           &regex),
     752            0 :         GNUNET_JSON_spec_string (
     753              :           "human_hint",
     754              :           &hint),
     755            0 :         GNUNET_JSON_spec_mark_optional (
     756              :           GNUNET_JSON_spec_json (
     757              :             "human_hint_i18n",
     758              :             &ar->details.regex.human_hint_i18n),
     759              :           NULL),
     760            0 :         GNUNET_JSON_spec_end ()
     761              :       };
     762              : 
     763            0 :       if (GNUNET_OK !=
     764            0 :           GNUNET_JSON_parse (jr,
     765              :                              spec,
     766              :                              NULL, NULL))
     767              :       {
     768              :         /* bogus reply */
     769            0 :         GNUNET_break_op (0);
     770            0 :         goto fail;
     771              :       }
     772            0 :       ar->type = TALER_EXCHANGE_AR_REGEX;
     773            0 :       ar->details.regex.posix_egrep = GNUNET_strdup (regex);
     774            0 :       ar->details.regex.human_hint = GNUNET_strdup (hint);
     775            0 :       continue;
     776              :     }
     777              :     /* unsupported type */
     778            0 :     GNUNET_break (0);
     779            0 :     goto fail;
     780              :   }
     781            0 :   return GNUNET_OK;
     782            0 : fail:
     783            0 :   for (unsigned int i = 0; i<*resta_len; i++)
     784              :   {
     785            0 :     struct TALER_EXCHANGE_AccountRestriction *ar = &(*resta)[i];
     786              : 
     787            0 :     if (TALER_EXCHANGE_AR_REGEX == ar->type)
     788              :     {
     789            0 :       GNUNET_free (ar->details.regex.posix_egrep);
     790            0 :       GNUNET_free (ar->details.regex.human_hint);
     791            0 :       json_decref (ar->details.regex.human_hint_i18n);
     792              :     }
     793              :   }
     794            0 :   GNUNET_free (*resta);
     795            0 :   *resta_len = 0;
     796            0 :   return GNUNET_SYSERR;
     797              : }
     798              : 
     799              : 
     800              : enum GNUNET_GenericReturnValue
     801           50 : TALER_EXCHANGE_parse_accounts (
     802              :   const struct TALER_MasterPublicKeyP *master_pub,
     803              :   const json_t *accounts,
     804              :   unsigned int was_length,
     805              :   struct TALER_EXCHANGE_WireAccount was[static was_length])
     806           50 : {
     807           50 :   memset (was,
     808              :           0,
     809              :           sizeof (struct TALER_EXCHANGE_WireAccount) * was_length);
     810           50 :   GNUNET_assert (was_length ==
     811              :                  json_array_size (accounts));
     812           50 :   for (unsigned int i = 0;
     813          104 :        i<was_length;
     814           54 :        i++)
     815              :   {
     816           54 :     struct TALER_EXCHANGE_WireAccount *wa = &was[i];
     817              :     struct TALER_FullPayto payto_uri;
     818           54 :     const char *conversion_url = NULL;
     819           54 :     const char *open_banking_gateway = NULL;
     820           54 :     const char *prepared_transfer_url = NULL;
     821           54 :     const char *bank_label = NULL;
     822           54 :     int64_t priority = 0;
     823              :     const json_t *credit_restrictions;
     824              :     const json_t *debit_restrictions;
     825              :     struct GNUNET_JSON_Specification spec_account[] = {
     826           54 :       TALER_JSON_spec_full_payto_uri ("payto_uri",
     827              :                                       &payto_uri),
     828           54 :       GNUNET_JSON_spec_mark_optional (
     829              :         TALER_JSON_spec_web_url ("conversion_url",
     830              :                                  &conversion_url),
     831              :         NULL),
     832           54 :       GNUNET_JSON_spec_mark_optional (
     833              :         TALER_JSON_spec_web_url ("open_banking_gateway",
     834              :                                  &open_banking_gateway),
     835              :         NULL),
     836           54 :       GNUNET_JSON_spec_mark_optional (
     837              :         TALER_JSON_spec_web_url ("prepared_transfer_url",
     838              :                                  &prepared_transfer_url),
     839              :         NULL),
     840           54 :       GNUNET_JSON_spec_mark_optional (
     841              :         GNUNET_JSON_spec_int64 ("priority",
     842              :                                 &priority),
     843              :         NULL),
     844           54 :       GNUNET_JSON_spec_mark_optional (
     845              :         GNUNET_JSON_spec_string ("bank_label",
     846              :                                  &bank_label),
     847              :         NULL),
     848           54 :       GNUNET_JSON_spec_array_const ("credit_restrictions",
     849              :                                     &credit_restrictions),
     850           54 :       GNUNET_JSON_spec_array_const ("debit_restrictions",
     851              :                                     &debit_restrictions),
     852           54 :       GNUNET_JSON_spec_fixed_auto ("master_sig",
     853              :                                    &wa->master_sig),
     854           54 :       GNUNET_JSON_spec_end ()
     855              :     };
     856              :     json_t *account;
     857              : 
     858           54 :     account = json_array_get (accounts,
     859              :                               i);
     860           54 :     if (GNUNET_OK !=
     861           54 :         GNUNET_JSON_parse (account,
     862              :                            spec_account,
     863              :                            NULL, NULL))
     864              :     {
     865              :       /* bogus reply */
     866            0 :       GNUNET_break_op (0);
     867            0 :       return GNUNET_SYSERR;
     868              :     }
     869           54 :     if ( (NULL != master_pub) &&
     870          108 :          (! ( ( (NULL == open_banking_gateway) &&
     871          108 :                 (NULL == prepared_transfer_url) &&
     872              :                 (GNUNET_OK ==
     873           54 :                  TALER_exchange_wire_signature_check32 (
     874              :                    payto_uri,
     875              :                    conversion_url,
     876              :                    debit_restrictions,
     877              :                    credit_restrictions,
     878              :                    master_pub,
     879           54 :                    &wa->master_sig)) ) ||
     880              :               (GNUNET_OK ==
     881           54 :                TALER_exchange_wire_signature_check (
     882              :                  payto_uri,
     883              :                  conversion_url,
     884              :                  open_banking_gateway,
     885              :                  prepared_transfer_url,
     886              :                  debit_restrictions,
     887              :                  credit_restrictions,
     888              :                  master_pub,
     889           54 :                  &wa->master_sig)) ) ) )
     890              :     {
     891              :       /* bogus reply */
     892            0 :       GNUNET_break_op (0);
     893            0 :       return GNUNET_SYSERR;
     894              :     }
     895           54 :     if ( (GNUNET_OK !=
     896           54 :           parse_restrictions (credit_restrictions,
     897              :                               &wa->credit_restrictions_length,
     898           54 :                               &wa->credit_restrictions)) ||
     899              :          (GNUNET_OK !=
     900           54 :           parse_restrictions (debit_restrictions,
     901              :                               &wa->debit_restrictions_length,
     902              :                               &wa->debit_restrictions)) )
     903              :     {
     904              :       /* bogus reply */
     905            0 :       GNUNET_break_op (0);
     906            0 :       return GNUNET_SYSERR;
     907              :     }
     908              :     wa->fpayto_uri.full_payto
     909           54 :       = GNUNET_strdup (payto_uri.full_payto);
     910           54 :     wa->priority = priority;
     911           54 :     if (NULL != conversion_url)
     912            0 :       wa->conversion_url = GNUNET_strdup (conversion_url);
     913           54 :     if (NULL != open_banking_gateway)
     914            0 :       wa->open_banking_gateway = GNUNET_strdup (open_banking_gateway);
     915           54 :     if (NULL != prepared_transfer_url)
     916            0 :       wa->prepared_transfer_url = GNUNET_strdup (prepared_transfer_url);
     917           54 :     if (NULL != bank_label)
     918            4 :       wa->bank_label = GNUNET_strdup (bank_label);
     919              :   }       /* end 'for all accounts */
     920           50 :   return GNUNET_OK;
     921              : }
     922              : 
     923              : 
     924              : /**
     925              :  * Free array of account restrictions.
     926              :  *
     927              :  * @param ar_len length of @a ar
     928              :  * @param[in] ar array to free contents of (but not @a ar itself)
     929              :  */
     930              : static void
     931          108 : free_restrictions (unsigned int ar_len,
     932              :                    struct TALER_EXCHANGE_AccountRestriction ar[static ar_len])
     933          108 : {
     934          108 :   for (unsigned int i = 0; i<ar_len; i++)
     935              :   {
     936            0 :     struct TALER_EXCHANGE_AccountRestriction *a = &ar[i];
     937            0 :     switch (a->type)
     938              :     {
     939            0 :     case TALER_EXCHANGE_AR_INVALID:
     940            0 :       GNUNET_break (0);
     941            0 :       break;
     942            0 :     case TALER_EXCHANGE_AR_DENY:
     943            0 :       break;
     944            0 :     case TALER_EXCHANGE_AR_REGEX:
     945            0 :       GNUNET_free (a->details.regex.posix_egrep);
     946            0 :       GNUNET_free (a->details.regex.human_hint);
     947            0 :       json_decref (a->details.regex.human_hint_i18n);
     948            0 :       break;
     949              :     }
     950              :   }
     951          108 : }
     952              : 
     953              : 
     954              : void
     955           50 : TALER_EXCHANGE_free_accounts (
     956              :   unsigned int was_len,
     957              :   struct TALER_EXCHANGE_WireAccount was[static was_len])
     958           50 : {
     959          104 :   for (unsigned int i = 0; i<was_len; i++)
     960              :   {
     961           54 :     struct TALER_EXCHANGE_WireAccount *wa = &was[i];
     962              : 
     963           54 :     GNUNET_free (wa->fpayto_uri.full_payto);
     964           54 :     GNUNET_free (wa->conversion_url);
     965           54 :     GNUNET_free (wa->open_banking_gateway);
     966           54 :     GNUNET_free (wa->prepared_transfer_url);
     967           54 :     GNUNET_free (wa->bank_label);
     968           54 :     free_restrictions (wa->credit_restrictions_length,
     969              :                        wa->credit_restrictions);
     970           54 :     GNUNET_array_grow (wa->credit_restrictions,
     971              :                        wa->credit_restrictions_length,
     972              :                        0);
     973           54 :     free_restrictions (wa->debit_restrictions_length,
     974              :                        wa->debit_restrictions);
     975           54 :     GNUNET_array_grow (wa->debit_restrictions,
     976              :                        wa->debit_restrictions_length,
     977              :                        0);
     978              :   }
     979           50 : }
     980              : 
     981              : 
     982              : enum GNUNET_GenericReturnValue
     983            0 : TALER_EXCHANGE_keys_test_account_allowed (
     984              :   const struct TALER_EXCHANGE_Keys *keys,
     985              :   bool check_credit,
     986              :   const struct TALER_NormalizedPayto payto_uri)
     987              : {
     988              :   /* For all accounts of the exchange */
     989            0 :   for (unsigned int i = 0; i<keys->accounts_len; i++)
     990              :   {
     991            0 :     const struct TALER_EXCHANGE_WireAccount *account
     992            0 :       = &keys->accounts[i];
     993              : 
     994              :     /* KYC auth transfers are never supported with conversion */
     995            0 :     if (NULL != account->conversion_url)
     996            0 :       continue;
     997              :     /* filter by source account by credit_restrictions */
     998            0 :     if (GNUNET_YES !=
     999            0 :         TALER_EXCHANGE_test_account_allowed (account,
    1000              :                                              check_credit,
    1001              :                                              payto_uri))
    1002            0 :       continue;
    1003              :     /* exchange account is allowed, add it */
    1004            0 :     return true;
    1005              :   }
    1006            0 :   return false;
    1007              : }
    1008              : 
    1009              : 
    1010              : /**
    1011              :  * We received an #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS response code.
    1012              :  * Parse the JSON response and initialize the @a uflr object.
    1013              :  *
    1014              :  * @param[out] uflr data structure to initialize
    1015              :  * @param j JSON response to parse
    1016              :  * @return #GNUNET_OK on success
    1017              :  */
    1018              : enum GNUNET_GenericReturnValue
    1019           20 : TALER_EXCHANGE_parse_451 (struct TALER_EXCHANGE_KycNeededRedirect *uflr,
    1020              :                           const json_t *j)
    1021              : {
    1022              :   struct GNUNET_JSON_Specification spec[] = {
    1023           20 :     GNUNET_JSON_spec_fixed_auto (
    1024              :       "h_payto",
    1025              :       &uflr->h_payto),
    1026           20 :     GNUNET_JSON_spec_uint64 (
    1027              :       "requirement_row",
    1028              :       &uflr->requirement_row),
    1029           20 :     GNUNET_JSON_spec_mark_optional (
    1030           20 :       GNUNET_JSON_spec_fixed_auto (
    1031              :         "account_pub",
    1032              :         &uflr->account_pub),
    1033              :       NULL),
    1034           20 :     GNUNET_JSON_spec_mark_optional (
    1035              :       GNUNET_JSON_spec_bool (
    1036              :         "bad_kyc_auth",
    1037              :         &uflr->bad_kyc_auth),
    1038              :       NULL),
    1039           20 :     GNUNET_JSON_spec_end ()
    1040              :   };
    1041              : 
    1042           20 :   if (GNUNET_OK !=
    1043           20 :       GNUNET_JSON_parse (j,
    1044              :                          spec,
    1045              :                          NULL,
    1046              :                          NULL))
    1047              :   {
    1048            0 :     GNUNET_break_op (0);
    1049            0 :     return GNUNET_SYSERR;
    1050              :   }
    1051           20 :   return GNUNET_OK;
    1052              : }
    1053              : 
    1054              : 
    1055              : json_t *
    1056           42 : TALER_EXCHANGE_recoup_coin_data_ (
    1057              :   size_t num_coins,
    1058              :   const struct TALER_EXCHANGE_RecoupCoin coins[static num_coins],
    1059              :   const struct TALER_BlindingMasterSeedP *blinding_seed,
    1060              :   bool for_melt,
    1061              :   struct TALER_CoinSpendPublicKeyP recouped_pubs[static num_coins],
    1062              :   struct TALER_EXCHANGE_RecoupedCoinInfo_ recouped_infos[static num_coins],
    1063              :   size_t *num_recouped)
    1064           42 : {
    1065           42 :   json_t *arr = json_array ();
    1066              : 
    1067           42 :   GNUNET_assert (NULL != arr);
    1068           42 :   *num_recouped = 0;
    1069          144 :   for (size_t i = 0; i < num_coins; i++)
    1070              :   {
    1071          102 :     const struct TALER_EXCHANGE_RecoupCoin *c = &coins[i];
    1072              :     struct TALER_CoinSpendPrivateKeyP coin_priv;
    1073              :     struct TALER_CoinSpendPublicKeyP coin_pub;
    1074              :     union GNUNET_CRYPTO_BlindingSecretP bks;
    1075              :     union GNUNET_CRYPTO_BlindSessionNonce nonce;
    1076          102 :     const union GNUNET_CRYPTO_BlindSessionNonce *np = NULL;
    1077              :     json_t *entry;
    1078              : 
    1079          102 :     const struct TALER_ExchangeBlindingValues *alg_values
    1080              :       = c->exchange_vals;
    1081              : 
    1082          102 :     if ( (NULL == c->pk) ||
    1083          102 :          (NULL == c->ps) )
    1084              :     {
    1085            0 :       GNUNET_break (0);
    1086            0 :       json_decref (arr);
    1087            0 :       return NULL;
    1088              :     }
    1089          102 :     if (NULL == alg_values)
    1090              :     {
    1091              :       /* documented for RSA denominations, which have no
    1092              :          exchange-contributed blinding values */
    1093           24 :       if (GNUNET_CRYPTO_BSA_RSA != c->pk->key.bsign_pub_key->cipher)
    1094              :       {
    1095            0 :         GNUNET_break (0);
    1096            0 :         json_decref (arr);
    1097            0 :         return NULL;
    1098              :       }
    1099           24 :       alg_values = TALER_denom_ewv_rsa_singleton ();
    1100              :     }
    1101          102 :     TALER_planchet_setup_coin_priv (c->ps,
    1102              :                                     alg_values,
    1103              :                                     &coin_priv);
    1104          102 :     TALER_planchet_blinding_secret_create (c->ps,
    1105              :                                            alg_values,
    1106              :                                            &bks);
    1107          102 :     GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv,
    1108              :                                         &coin_pub.eddsa_pub);
    1109          102 :     if (GNUNET_CRYPTO_BSA_CS == c->pk->key.bsign_pub_key->cipher)
    1110              :     {
    1111           51 :       if (NULL == blinding_seed)
    1112              :       {
    1113            0 :         GNUNET_break (0);
    1114            0 :         json_decref (arr);
    1115            0 :         return NULL;
    1116              :       }
    1117           51 :       TALER_cs_nonce_derive_indexed (blinding_seed,
    1118              :                                      for_melt,
    1119              :                                      (uint32_t) i,
    1120              :                                      &nonce.cs_nonce);
    1121           51 :       np = &nonce;
    1122              :     }
    1123          102 :     if (NULL == c->denom_sig)
    1124              :     {
    1125              :       struct TALER_PlanchetDetail pd;
    1126              :       struct TALER_CoinPubHashP c_hash;
    1127              :       struct TALER_BlindedCoinHashP bch;
    1128              : 
    1129           54 :       if (GNUNET_OK !=
    1130           54 :           TALER_planchet_prepare (&c->pk->key,
    1131              :                                   alg_values,
    1132              :                                   &bks,
    1133              :                                   np,
    1134              :                                   &coin_priv,
    1135           54 :                                   c->h_age_commitment,
    1136              :                                   &c_hash,
    1137              :                                   &pd))
    1138              :       {
    1139            0 :         GNUNET_break (0);
    1140            0 :         json_decref (arr);
    1141            0 :         return NULL;
    1142              :       }
    1143           54 :       TALER_coin_ev_hash (&pd.blinded_planchet,
    1144           54 :                           &c->pk->h_key,
    1145              :                           &bch);
    1146           54 :       TALER_blinded_planchet_free (&pd.blinded_planchet);
    1147           54 :       entry = GNUNET_JSON_PACK (
    1148              :         GNUNET_JSON_pack_string ("type",
    1149              :                                  "hash"),
    1150              :         GNUNET_JSON_pack_data_auto ("h_coin_ev",
    1151              :                                     &bch));
    1152              :     }
    1153              :     else
    1154              :     {
    1155              :       struct TALER_CoinSpendSignatureP coin_sig;
    1156              : 
    1157           48 :       if (for_melt)
    1158           20 :         TALER_wallet_recoup_refresh_sign (&c->pk->h_key,
    1159              :                                           &bks,
    1160              :                                           &coin_priv,
    1161              :                                           &coin_sig);
    1162              :       else
    1163           28 :         TALER_wallet_recoup_sign (&c->pk->h_key,
    1164              :                                   &bks,
    1165              :                                   &coin_priv,
    1166              :                                   &coin_sig);
    1167           48 :       entry = GNUNET_JSON_PACK (
    1168              :         GNUNET_JSON_pack_string ("type",
    1169              :                                  "recoup"),
    1170              :         GNUNET_JSON_pack_data_auto ("coin_pub",
    1171              :                                     &coin_pub),
    1172              :         GNUNET_JSON_pack_data_auto ("denom_pub_h",
    1173              :                                     &c->pk->h_key),
    1174              :         TALER_JSON_pack_denom_sig ("denom_sig",
    1175              :                                    c->denom_sig),
    1176              :         GNUNET_JSON_pack_data_auto ("coin_blinding_secret",
    1177              :                                     &bks),
    1178              :         GNUNET_JSON_pack_allow_null (
    1179              :           GNUNET_JSON_pack_data_varsize ("h_age_commitment",
    1180              :                                          c->h_age_commitment,
    1181              :                                          (NULL == c->h_age_commitment)
    1182              :                                          ? 0
    1183              :                                          : sizeof (*c->h_age_commitment))),
    1184              :         GNUNET_JSON_pack_data_auto ("coin_sig",
    1185              :                                     &coin_sig));
    1186           48 :       recouped_pubs[*num_recouped] = coin_pub;
    1187           48 :       recouped_infos[*num_recouped].h_denom_pub = c->pk->h_key;
    1188           48 :       recouped_infos[*num_recouped].have_age = (NULL != c->h_age_commitment);
    1189           48 :       if (NULL != c->h_age_commitment)
    1190            4 :         recouped_infos[*num_recouped].h_age_commitment = *c->h_age_commitment;
    1191           48 :       (*num_recouped)++;
    1192              :     }
    1193          102 :     GNUNET_assert (0 ==
    1194              :                    json_array_append_new (arr,
    1195              :                                           entry));
    1196              :   }
    1197           42 :   if (0 == *num_recouped)
    1198              :   {
    1199            0 :     GNUNET_break (0);
    1200            0 :     json_decref (arr);
    1201            0 :     return NULL;
    1202              :   }
    1203           42 :   return arr;
    1204              : }
    1205              : 
    1206              : 
    1207              : enum GNUNET_GenericReturnValue
    1208           36 : TALER_EXCHANGE_parse_recoups_ (
    1209              :   const json_t *j_recoups,
    1210              :   size_t num_expected,
    1211              :   const struct TALER_CoinSpendPublicKeyP expected_pubs[static num_expected],
    1212              :   const struct TALER_Amount *total_amount,
    1213              :   struct TALER_RecoupedCoin **recoups,
    1214              :   struct GNUNET_HashCode *h_recoups)
    1215           36 : {
    1216              :   struct TALER_RecoupedCoin *rcs;
    1217              :   struct TALER_Amount sum;
    1218              :   size_t idx;
    1219              :   json_t *entry;
    1220              : 
    1221           36 :   *recoups = NULL;
    1222           72 :   if ( (! json_is_array (j_recoups)) ||
    1223           36 :        (num_expected != json_array_size (j_recoups)) )
    1224              :   {
    1225            0 :     GNUNET_break_op (0);
    1226            0 :     return GNUNET_SYSERR;
    1227              :   }
    1228           36 :   rcs = GNUNET_new_array (num_expected,
    1229              :                           struct TALER_RecoupedCoin);
    1230           36 :   GNUNET_assert (GNUNET_OK ==
    1231              :                  TALER_amount_set_zero (total_amount->currency,
    1232              :                                         &sum));
    1233           78 :   json_array_foreach (j_recoups, idx, entry)
    1234              :   {
    1235              :     struct GNUNET_JSON_Specification spec[] = {
    1236           42 :       GNUNET_JSON_spec_fixed_auto ("coin_pub",
    1237              :                                    &rcs[idx].coin_pub),
    1238           42 :       TALER_JSON_spec_amount_any ("amount",
    1239           42 :                                   &rcs[idx].amount),
    1240           42 :       GNUNET_JSON_spec_end ()
    1241              :     };
    1242              : 
    1243           42 :     if (GNUNET_OK !=
    1244           42 :         GNUNET_JSON_parse (entry,
    1245              :                            spec,
    1246              :                            NULL, NULL))
    1247              :     {
    1248            0 :       GNUNET_break_op (0);
    1249            0 :       GNUNET_free (rcs);
    1250            0 :       return GNUNET_SYSERR;
    1251              :     }
    1252           42 :     if (0 !=
    1253           42 :         GNUNET_memcmp (&rcs[idx].coin_pub,
    1254              :                        &expected_pubs[idx]))
    1255              :     {
    1256            0 :       GNUNET_break_op (0);
    1257            0 :       GNUNET_free (rcs);
    1258            0 :       return GNUNET_SYSERR;
    1259              :     }
    1260           42 :     if ( (GNUNET_OK !=
    1261           42 :           TALER_amount_cmp_currency (&sum,
    1262           84 :                                      &rcs[idx].amount)) ||
    1263              :          (0 >
    1264           42 :           TALER_amount_add (&sum,
    1265              :                             &sum,
    1266           42 :                             &rcs[idx].amount)) )
    1267              :     {
    1268            0 :       GNUNET_break_op (0);
    1269            0 :       GNUNET_free (rcs);
    1270            0 :       return GNUNET_SYSERR;
    1271              :     }
    1272              :   }
    1273           36 :   if ( (GNUNET_OK !=
    1274           36 :         TALER_amount_cmp_currency (&sum,
    1275           36 :                                    total_amount)) ||
    1276              :        (0 !=
    1277           36 :         TALER_amount_cmp (&sum,
    1278              :                           total_amount)) )
    1279              :   {
    1280            0 :     GNUNET_break_op (0);
    1281            0 :     GNUNET_free (rcs);
    1282            0 :     return GNUNET_SYSERR;
    1283              :   }
    1284           36 :   TALER_recoup_batch_hash (num_expected,
    1285              :                            rcs,
    1286              :                            h_recoups);
    1287           36 :   *recoups = rcs;
    1288           36 :   return GNUNET_OK;
    1289              : }
    1290              : 
    1291              : 
    1292              : /* end of exchange_api_common.c */
        

Generated by: LCOV version 2.0-1