LCOV - code coverage report
Current view: top level - lib - exchange_api_withdraw.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 488 589 82.9 %
Date: 2025-06-05 21:03:14 Functions: 19 19 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2023-2025 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_withdraw.c
      19             :  * @brief Implementation of /withdraw requests
      20             :  * @author Özgür Kesim
      21             :  */
      22             : /**
      23             :  * We want the "dangerous" exports here as these are OUR exports
      24             :  * and we want to check that the prototypes match.
      25             :  */
      26             : #define TALER_TESTING_EXPORTS_DANGEROUS 1
      27             : #include "platform.h"
      28             : #include <gnunet/gnunet_common.h>
      29             : #include <jansson.h>
      30             : #include <microhttpd.h> /* just for HTTP status codes */
      31             : #include <gnunet/gnunet_util_lib.h>
      32             : #include <gnunet/gnunet_json_lib.h>
      33             : #include <gnunet/gnunet_curl_lib.h>
      34             : #include <sys/wait.h>
      35             : #include "taler_curl_lib.h"
      36             : #include "taler_error_codes.h"
      37             : #include "taler_json_lib.h"
      38             : #include "taler_exchange_service.h"
      39             : #include "exchange_api_common.h"
      40             : #include "exchange_api_handle.h"
      41             : #include "taler_signatures.h"
      42             : #include "exchange_api_curl_defaults.h"
      43             : #include "taler_util.h"
      44             : 
      45             : /**
      46             :  * A CoinCandidate is populated from a master secret.
      47             :  * The data is copied from and generated out of the client's input.
      48             :  */
      49             : struct CoinCandidate
      50             : {
      51             :   /**
      52             :    * The details derived form the master secrets
      53             :    */
      54             :   struct TALER_EXCHANGE_WithdrawCoinPrivateDetails details;
      55             : 
      56             :   /**
      57             :    * Blinded hash of the coin
      58             :    **/
      59             :   struct TALER_BlindedCoinHashP blinded_coin_h;
      60             : 
      61             : };
      62             : 
      63             : 
      64             : /**
      65             :  * Closure for a call to /blinding-prepare, contains data that is needed to process
      66             :  * the result.
      67             :  */
      68             : struct BlindingPrepareClosure
      69             : {
      70             :   /**
      71             :    * Number of coins in the blinding-prepare step.
      72             :    * Not that this number might be smaller than the total number
      73             :    * of coins in the withdraw, as the prepare is only necessary
      74             :    * for CS denominations
      75             :    */
      76             :   size_t num_prepare_coins;
      77             : 
      78             :   /**
      79             :    * Array of @e num_prepare_coins of data per coin
      80             :    */
      81             :   struct BlindingPrepareCoinData
      82             :   {
      83             :     /**
      84             :      * Pointer to the candidate in CoinData.candidates,
      85             :      * to continue to build its contents based on the results from /blinding-prepare
      86             :      */
      87             :     struct CoinCandidate *candidate;
      88             : 
      89             :     /**
      90             :      * Planchet to finally generate in the corresponding candidate
      91             :      * in CoindData.planchet_details
      92             :      */
      93             :     struct TALER_PlanchetDetail *planchet;
      94             : 
      95             :     /**
      96             :      * Denomination information, needed for the
      97             :      * step after /blinding-prepare
      98             :      */
      99             :     const struct TALER_DenominationPublicKey *denom_pub;
     100             : 
     101             :     /**
     102             :      * True, if denomination supports age restriction
     103             :      */
     104             :     bool age_denom;
     105             : 
     106             :     /**
     107             :      * The index into the array of returned values from the call to
     108             :      * /blinding-prepare that are to be used for this coin.
     109             :      */
     110             :     size_t cs_idx;
     111             : 
     112             :   } *coins;
     113             : 
     114             :   /**
     115             :    * Number of seeds requested.  This may differ from @e num_prepare_coins
     116             :    * in case of a withdraw with required age proof, in which case
     117             :    * @e num_prepare_coins = TALER_CNC_KAPPA * @e num_seeds
     118             :    */
     119             :   size_t num_nonces;
     120             : 
     121             :   /**
     122             :    * Array of @e num_nonces calculated nonces.
     123             :    */
     124             :   union GNUNET_CRYPTO_BlindSessionNonce *nonces;
     125             : 
     126             :   /**
     127             :    * Handler to the originating call to /withdraw, needed to either
     128             :    * cancel the running withdraw request (on failure of the current call
     129             :    * to /blinding-prepare), or to eventually perform the protocol, once all
     130             :    * blinding-prepare requests have successfully finished.
     131             :    */
     132             :   struct TALER_EXCHANGE_WithdrawHandle *withdraw_handle;
     133             : 
     134             : };
     135             : 
     136             : 
     137             : /**
     138             :  * Data we keep per coin in the batch.
     139             :  * This is copied from and generated out of the input provided
     140             :  * by the client.
     141             :  */
     142             : struct CoinData
     143             : {
     144             :   /**
     145             :    * The denomination of the coin.
     146             :    */
     147             :   struct TALER_EXCHANGE_DenomPublicKey denom_pub;
     148             : 
     149             :   /**
     150             :    * The Candidates for the coin.  If the batch is not age-restricted,
     151             :    * only index 0 is used.
     152             :    */
     153             :   struct CoinCandidate candidates[TALER_CNC_KAPPA];
     154             : 
     155             :   /**
     156             :    * Details of the planchet(s).  If the batch is not age-restricted,
     157             :    * only index 0 is used.
     158             :    */
     159             :   struct TALER_PlanchetDetail planchet_details[TALER_CNC_KAPPA];
     160             : };
     161             : 
     162             : 
     163             : /**
     164             :  * A /withdraw request-handle for calls with pre-blinded planchets.
     165             :  * Returned by TALER_EXCHANGE_withdraw_blinded.
     166             :  */
     167             : struct TALER_EXCHANGE_WithdrawBlindedHandle
     168             : {
     169             : 
     170             :   /**
     171             :    * Reserve private key.
     172             :    */
     173             :   const struct TALER_ReservePrivateKeyP *reserve_priv;
     174             : 
     175             :   /**
     176             :    * Reserve public key, calculated
     177             :    */
     178             :   struct TALER_ReservePublicKeyP reserve_pub;
     179             : 
     180             :   /**
     181             :    * Signature of the reserve for the request, calculated after all
     182             :    * parameters for the coins are collected.
     183             :    */
     184             :   struct TALER_ReserveSignatureP reserve_sig;
     185             : 
     186             :   /*
     187             :    * The denomination keys of the exchange
     188             :    */
     189             :   struct TALER_EXCHANGE_Keys *keys;
     190             : 
     191             :   /**
     192             :    * The hash of all the planchets
     193             :    */
     194             :   struct TALER_HashBlindedPlanchetsP planchets_h;
     195             : 
     196             :   /**
     197             :    * Seed used for the derival of blinding factors for denominations
     198             :    * with Clause-Schnorr cipher.  We derive this from the master seed
     199             :    * for the withdraw, but independent from the other planchet seeds.
     200             :    */
     201             :   const struct TALER_BlindingMasterSeedP *blinding_seed;
     202             : 
     203             :   /**
     204             :    * Total amount requested (without fee).
     205             :    */
     206             :   struct TALER_Amount amount;
     207             : 
     208             :   /**
     209             :    * Total withdraw fee
     210             :    */
     211             :   struct TALER_Amount fee;
     212             : 
     213             :   /**
     214             :    * Is this call for age-restriced coins, with age proof?
     215             :    */
     216             :   bool with_age_proof;
     217             : 
     218             :   /**
     219             :    * If @e with_age_proof is true or @max_age is > 0,
     220             :    *  the age mask to use, extracted from the denominations.
     221             :    * MUST be the same for all denominations.
     222             :    */
     223             :   struct TALER_AgeMask age_mask;
     224             : 
     225             :   /**
     226             :    * The maximum age to commit to.  If @e with_age_proof
     227             :    * is true, the client will need to proof the correct setting
     228             :    * of age-restriction on the coins via an additional call
     229             :    * to /reveal-withdraw.
     230             :    */
     231             :   uint8_t max_age;
     232             : 
     233             :   /**
     234             :    * If @e with_age_proof is true, the hash of all the selected planchets
     235             :    */
     236             :   struct TALER_HashBlindedPlanchetsP selected_h;
     237             : 
     238             :   /**
     239             :    * Length of the either the @e blinded.input or
     240             :    * the @e blinded.with_age_proof_input array,
     241             :    * depending on @e with_age_proof.
     242             :    */
     243             :   size_t num_input;
     244             : 
     245             :   union
     246             :   {
     247             :     /**
     248             :      * The blinded planchet input candidates for age-restricted coins
     249             :      * for the call to /withdraw
     250             :      */
     251             :     const struct
     252             :     TALER_EXCHANGE_WithdrawBlindedAgeRestrictedCoinInput *with_age_proof_input;
     253             : 
     254             :     /**
     255             :      * The blinded planchet input for the call to /withdraw via
     256             :      * TALER_EXCHANGE_withdraw_blinded, for age-unrestricted coins.
     257             :      */
     258             :     const struct TALER_EXCHANGE_WithdrawBlindedCoinInput *input;
     259             : 
     260             :   } blinded;
     261             : 
     262             :   /**
     263             :    * The url for this request.
     264             :    */
     265             :   char *request_url;
     266             : 
     267             :   /**
     268             :    * Context for curl.
     269             :    */
     270             :   struct GNUNET_CURL_Context *curl_ctx;
     271             : 
     272             :   /**
     273             :    * CURL handle for the request job.
     274             :    */
     275             :   struct GNUNET_CURL_Job *job;
     276             : 
     277             :   /**
     278             :    * Post Context
     279             :    */
     280             :   struct TALER_CURL_PostContext post_ctx;
     281             : 
     282             :   /**
     283             :    * Function to call with withdraw response results.
     284             :    */
     285             :   TALER_EXCHANGE_WithdrawBlindedCallback callback;
     286             : 
     287             :   /**
     288             :    * Closure for @e blinded_callback
     289             :    */
     290             :   void *callback_cls;
     291             : };
     292             : 
     293             : /**
     294             :  * A /withdraw request-handle for calls from
     295             :  * a wallet, i. e. when blinding data is available.
     296             :  */
     297             : struct TALER_EXCHANGE_WithdrawHandle
     298             : {
     299             : 
     300             :   /**
     301             :    * The base-URL of the exchange.
     302             :    */
     303             :   const char *exchange_url;
     304             : 
     305             :   /**
     306             :    * Seed to derive of all seeds for the coins.
     307             :    */
     308             :   struct TALER_WithdrawMasterSeedP seed;
     309             : 
     310             :   /**
     311             :    * If @e with_age_proof is true, the derived TALER_CNC_KAPPA many
     312             :    * seeds for candidate batches.
     313             :    */
     314             :   struct TALER_KappaWithdrawMasterSeedP kappa_seed;
     315             : 
     316             :   /**
     317             :    * True if @e blinding_seed is filled, that is, if
     318             :    * any of the denominations is of cipher type CS
     319             :    */
     320             :   bool has_blinding_seed;
     321             : 
     322             :   /**
     323             :    * Seed used for the derivation of blinding factors for denominations
     324             :    * with Clause-Schnorr cipher.  We derive this from the master seed
     325             :    * for the withdraw, but independent from the other planchet seeds.
     326             :    * Only valid when @e has_blinding_seed is true;
     327             :    */
     328             :   struct TALER_BlindingMasterSeedP blinding_seed;
     329             : 
     330             :   /**
     331             :    * Reserve private key.
     332             :    */
     333             :   const struct TALER_ReservePrivateKeyP *reserve_priv;
     334             : 
     335             :   /**
     336             :    * Reserve public key, calculated
     337             :    */
     338             :   struct TALER_ReservePublicKeyP reserve_pub;
     339             : 
     340             :   /**
     341             :    * Signature of the reserve for the request, calculated after all
     342             :    * parameters for the coins are collected.
     343             :    */
     344             :   struct TALER_ReserveSignatureP reserve_sig;
     345             : 
     346             :   /*
     347             :    * The denomination keys of the exchange
     348             :    */
     349             :   struct TALER_EXCHANGE_Keys *keys;
     350             : 
     351             :   /**
     352             :    * True, if the withdraw is for age-restricted coins, with age-proof.
     353             :    * The denominations MUST support age restriction.
     354             :    */
     355             :   bool with_age_proof;
     356             : 
     357             :   /**
     358             :    * If @e with_age_proof is true, the age mask, extracted
     359             :    * from the denominations.
     360             :    * MUST be the same for all denominations.
     361             :    *
     362             :    */
     363             :   struct TALER_AgeMask age_mask;
     364             : 
     365             :   /**
     366             :    * The maximum age to commit to.  If @e with_age_proof
     367             :    * is true, the client will need to proof the correct setting
     368             :    * of age-restriction on the coins via an additional call
     369             :    * to /reveal-withdraw.
     370             :    */
     371             :   uint8_t max_age;
     372             : 
     373             :   /**
     374             :    * Length of the @e coin_data Array
     375             :    */
     376             :   size_t num_coins;
     377             : 
     378             :   /**
     379             :    * Array of per-coin data
     380             :    */
     381             :   struct CoinData *coin_data;
     382             : 
     383             :   /**
     384             :    * Context for curl.
     385             :    */
     386             :   struct GNUNET_CURL_Context *curl_ctx;
     387             : 
     388             :   /**
     389             :    * Function to call with withdraw response results.
     390             :    */
     391             :   TALER_EXCHANGE_WithdrawCallback callback;
     392             : 
     393             :   /**
     394             :    * Closure for @e callback
     395             :    */
     396             :   void *callback_cls;
     397             : 
     398             :   /* The handler for the call to /blinding-prepare, needed for CS denominations */
     399             :   struct TALER_EXCHANGE_BlindingPrepareHandle *blinding_prepare_handle;
     400             : 
     401             :   /* The Handler for the actual call to the exchange */
     402             :   struct TALER_EXCHANGE_WithdrawBlindedHandle *withdraw_blinded_handle;
     403             : };
     404             : 
     405             : 
     406             : /**
     407             :  * We got a 200 OK response for the /withdraw operation.
     408             :  * Extract the signatures and return them to the caller.
     409             :  *
     410             :  * @param wbh operation handle
     411             :  * @param j_response reply from the exchange
     412             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
     413             :  */
     414             : static enum GNUNET_GenericReturnValue
     415          61 : withdraw_blinded_ok (
     416             :   struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh,
     417             :   const json_t *j_response)
     418             : {
     419          61 :   struct TALER_EXCHANGE_WithdrawBlindedResponse response = {
     420             :     .hr.reply = j_response,
     421             :     .hr.http_status = MHD_HTTP_OK,
     422             :   };
     423             :   const json_t *j_sigs;
     424             :   struct GNUNET_JSON_Specification spec[] = {
     425          61 :     GNUNET_JSON_spec_array_const ("ev_sigs",
     426             :                                   &j_sigs),
     427          61 :     GNUNET_JSON_spec_end ()
     428             :   };
     429             : 
     430          61 :   if (GNUNET_OK !=
     431          61 :       GNUNET_JSON_parse (j_response,
     432             :                          spec,
     433             :                          NULL, NULL))
     434             :   {
     435           0 :     GNUNET_break_op (0);
     436           0 :     return GNUNET_SYSERR;
     437             :   }
     438             : 
     439          61 :   if (wbh->num_input != json_array_size (j_sigs))
     440             :   {
     441             :     /* Number of coins generated does not match our expectation */
     442           0 :     GNUNET_break_op (0);
     443           0 :     return GNUNET_SYSERR;
     444             :   }
     445             : 
     446          61 :   {
     447          61 :     struct TALER_BlindedDenominationSignature denoms_sig[wbh->num_input];
     448             : 
     449          61 :     memset (denoms_sig,
     450             :             0,
     451             :             sizeof(denoms_sig));
     452             : 
     453             :     /* Reconstruct the coins and unblind the signatures */
     454             :     {
     455             :       json_t *j_sig;
     456             :       size_t i;
     457             : 
     458         124 :       json_array_foreach (j_sigs, i, j_sig)
     459             :       {
     460             :         struct GNUNET_JSON_Specification ispec[] = {
     461          63 :           TALER_JSON_spec_blinded_denom_sig (NULL,
     462             :                                              &denoms_sig[i]),
     463          63 :           GNUNET_JSON_spec_end ()
     464             :         };
     465             : 
     466          63 :         if (GNUNET_OK !=
     467          63 :             GNUNET_JSON_parse (j_sig,
     468             :                                ispec,
     469             :                                NULL, NULL))
     470             :         {
     471           0 :           GNUNET_break_op (0);
     472           0 :           return GNUNET_SYSERR;
     473             :         }
     474             :       }
     475             :     }
     476             : 
     477          61 :     response.details.ok.num_sigs = wbh->num_input;
     478          61 :     response.details.ok.blinded_denom_sigs = denoms_sig;
     479          61 :     response.details.ok.planchets_h = wbh->planchets_h;
     480          61 :     wbh->callback (
     481             :       wbh->callback_cls,
     482             :       &response);
     483             :     /* Make sure the callback isn't called again */
     484          61 :     wbh->callback = NULL;
     485             :     /* Free resources */
     486         124 :     for (size_t i = 0; i < wbh->num_input; i++)
     487          63 :       TALER_blinded_denom_sig_free (&denoms_sig[i]);
     488             :   }
     489             : 
     490          61 :   return GNUNET_OK;
     491             : }
     492             : 
     493             : 
     494             : /**
     495             :  * We got a 201 CREATED response for the /withdraw operation.
     496             :  * Extract the noreveal_index and return it to the caller.
     497             :  *
     498             :  * @param wbh operation handle
     499             :  * @param j_response reply from the exchange
     500             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
     501             :  */
     502             : static enum GNUNET_GenericReturnValue
     503           3 : withdraw_blinded_created (
     504             :   struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh,
     505             :   const json_t *j_response)
     506             : {
     507           3 :   struct TALER_EXCHANGE_WithdrawBlindedResponse response = {
     508             :     .hr.reply = j_response,
     509             :     .hr.http_status = MHD_HTTP_CREATED,
     510             :     .details.created.planchets_h = wbh->planchets_h,
     511           3 :     .details.created.num_coins = wbh->num_input,
     512             :   };
     513             :   struct TALER_ExchangeSignatureP exchange_sig;
     514             :   struct GNUNET_JSON_Specification spec[] = {
     515           3 :     GNUNET_JSON_spec_uint8 ("noreveal_index",
     516             :                             &response.details.created.noreveal_index),
     517           3 :     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     518             :                                  &exchange_sig),
     519           3 :     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     520             :                                  &response.details.created.exchange_pub),
     521           3 :     GNUNET_JSON_spec_end ()
     522             :   };
     523             : 
     524           3 :   if (GNUNET_OK!=
     525           3 :       GNUNET_JSON_parse (j_response,
     526             :                          spec,
     527             :                          NULL, NULL))
     528             :   {
     529           0 :     GNUNET_break_op (0);
     530           0 :     return GNUNET_SYSERR;
     531             :   }
     532             : 
     533           3 :   if (GNUNET_OK !=
     534           3 :       TALER_exchange_online_withdraw_age_confirmation_verify (
     535           3 :         &wbh->planchets_h,
     536           3 :         response.details.created.noreveal_index,
     537             :         &response.details.created.exchange_pub,
     538             :         &exchange_sig))
     539             :   {
     540           0 :     GNUNET_break_op (0);
     541           0 :     return GNUNET_SYSERR;
     542             : 
     543             :   }
     544             : 
     545           3 :   wbh->callback (wbh->callback_cls,
     546             :                  &response);
     547             :   /* make sure the callback isn't called again */
     548           3 :   wbh->callback = NULL;
     549             : 
     550           3 :   return GNUNET_OK;
     551             : }
     552             : 
     553             : 
     554             : /**
     555             :  * Function called when we're done processing the
     556             :  * HTTP /withdraw request.
     557             :  *
     558             :  * @param cls the `struct TALER_EXCHANGE_WithdrawBlindedHandle`
     559             :  * @param response_code The HTTP response code
     560             :  * @param response response data
     561             :  */
     562             : static void
     563          75 : handle_withdraw_blinded_finished (
     564             :   void *cls,
     565             :   long response_code,
     566             :   const void *response)
     567             : {
     568          75 :   struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh = cls;
     569          75 :   const json_t *j_response = response;
     570          75 :   struct TALER_EXCHANGE_WithdrawBlindedResponse wbr = {
     571             :     .hr.reply = j_response,
     572          75 :     .hr.http_status = (unsigned int) response_code
     573             :   };
     574             : 
     575          75 :   wbh->job = NULL;
     576          75 :   switch (response_code)
     577             :   {
     578           0 :   case 0:
     579           0 :     wbr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     580           0 :     break;
     581          61 :   case MHD_HTTP_OK:
     582             :     {
     583          61 :       if (GNUNET_OK !=
     584          61 :           withdraw_blinded_ok (
     585             :             wbh,
     586             :             j_response))
     587             :       {
     588           0 :         GNUNET_break_op (0);
     589           0 :         wbr.hr.http_status = 0;
     590           0 :         wbr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     591           0 :         break;
     592             :       }
     593          61 :       GNUNET_assert (NULL == wbh->callback);
     594          61 :       TALER_EXCHANGE_withdraw_blinded_cancel (wbh);
     595          64 :       return;
     596             :     }
     597           3 :   case MHD_HTTP_CREATED:
     598           3 :     if (GNUNET_OK !=
     599           3 :         withdraw_blinded_created (
     600             :           wbh,
     601             :           j_response))
     602             :     {
     603           0 :       GNUNET_break_op (0);
     604           0 :       wbr.hr.http_status = 0;
     605           0 :       wbr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     606           0 :       break;
     607             :     }
     608           3 :     GNUNET_assert (NULL == wbh->callback);
     609           3 :     TALER_EXCHANGE_withdraw_blinded_cancel (wbh);
     610           3 :     return;
     611           0 :   case MHD_HTTP_BAD_REQUEST:
     612             :     /* This should never happen, either us or the exchange is buggy
     613             :        (or API version conflict); just pass JSON reply to the application */
     614           0 :     wbr.hr.ec = TALER_JSON_get_error_code (j_response);
     615           0 :     wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
     616           0 :     break;
     617           0 :   case MHD_HTTP_FORBIDDEN:
     618           0 :     GNUNET_break_op (0);
     619             :     /* Nothing really to verify, exchange says one of the signatures is
     620             :        invalid; as we checked them, this should never happen, we
     621             :        should pass the JSON reply to the application */
     622           0 :     wbr.hr.ec = TALER_JSON_get_error_code (j_response);
     623           0 :     wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
     624           0 :     break;
     625           0 :   case MHD_HTTP_NOT_FOUND:
     626             :     /* Nothing really to verify, the exchange basically just says
     627             :        that it doesn't know this reserve.  Can happen if we
     628             :        query before the wire transfer went through.
     629             :        We should simply pass the JSON reply to the application. */
     630           0 :     wbr.hr.ec = TALER_JSON_get_error_code (j_response);
     631           0 :     wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
     632           0 :     break;
     633           7 :   case MHD_HTTP_CONFLICT:
     634             :     /* The age requirements might not have been met */
     635           7 :     wbr.hr.ec = TALER_JSON_get_error_code (j_response);
     636           7 :     wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
     637           7 :     break;
     638           0 :   case MHD_HTTP_GONE:
     639             :     /* could happen if denomination was revoked */
     640             :     /* Note: one might want to check /keys for revocation
     641             :        signature here, alas tricky in case our /keys
     642             :        is outdated => left to clients */
     643           0 :     wbr.hr.ec = TALER_JSON_get_error_code (j_response);
     644           0 :     wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
     645           0 :     break;
     646           4 :   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
     647             :     /* only validate reply is well-formed */
     648             :     {
     649             :       struct GNUNET_JSON_Specification spec[] = {
     650           4 :         GNUNET_JSON_spec_fixed_auto (
     651             :           "h_payto",
     652             :           &wbr.details.unavailable_for_legal_reasons.h_payto),
     653           4 :         GNUNET_JSON_spec_uint64 (
     654             :           "requirement_row",
     655             :           &wbr.details.unavailable_for_legal_reasons.requirement_row),
     656           4 :         GNUNET_JSON_spec_end ()
     657             :       };
     658             : 
     659           4 :       if (GNUNET_OK !=
     660           4 :           GNUNET_JSON_parse (j_response,
     661             :                              spec,
     662             :                              NULL, NULL))
     663             :       {
     664           0 :         GNUNET_break_op (0);
     665           0 :         wbr.hr.http_status = 0;
     666           0 :         wbr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     667           0 :         break;
     668             :       }
     669           4 :       break;
     670             :     }
     671           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     672             :     /* Server had an internal issue; we should retry, but this API
     673             :        leaves this to the application */
     674           0 :     wbr.hr.ec = TALER_JSON_get_error_code (j_response);
     675           0 :     wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
     676           0 :     break;
     677           0 :   default:
     678             :     /* unexpected response code */
     679           0 :     GNUNET_break_op (0);
     680           0 :     wbr.hr.ec = TALER_JSON_get_error_code (j_response);
     681           0 :     wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
     682           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     683             :                 "Unexpected response code %u/%d for exchange withdraw\n",
     684             :                 (unsigned int) response_code,
     685             :                 (int) wbr.hr.ec);
     686           0 :     break;
     687             :   }
     688          11 :   wbh->callback (wbh->callback_cls,
     689             :                  &wbr);
     690          11 :   TALER_EXCHANGE_withdraw_blinded_cancel (wbh);
     691             : }
     692             : 
     693             : 
     694             : /**
     695             :  * Runs the actual withdraw operation with the blinded planchets.
     696             :  *
     697             :  * @param[in,out] wbh  withdraw blinded handle
     698             :  */
     699             : static void
     700          75 : perform_withdraw_protocol (
     701             :   struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh)
     702             : {
     703             : #define FAIL_IF(cond) \
     704             :         do { \
     705             :           if ((cond)) \
     706             :           { \
     707             :             GNUNET_break (! (cond)); \
     708             :             goto ERROR; \
     709             :           } \
     710             :         } while (0)
     711             : 
     712          75 :   json_t *j_denoms = NULL;
     713          75 :   json_t *j_planchets = NULL;
     714          75 :   json_t *j_request_body = NULL;
     715          75 :   CURL *curlh = NULL;
     716          75 :   struct GNUNET_HashContext *coins_hctx = NULL;
     717             :   struct TALER_BlindedCoinHashP bch;
     718             : 
     719          75 :   GNUNET_assert (0 < wbh->num_input);
     720             : 
     721          75 :   FAIL_IF (GNUNET_OK !=
     722             :            TALER_amount_set_zero (wbh->keys->currency,
     723             :                                   &wbh->amount));
     724          75 :   FAIL_IF (GNUNET_OK !=
     725             :            TALER_amount_set_zero (wbh->keys->currency,
     726             :                                   &wbh->fee));
     727             : 
     728             :   /* Accumulate total value with fees */
     729         156 :   for (size_t i = 0; i < wbh->num_input; i++)
     730             :   {
     731          81 :     const struct TALER_EXCHANGE_DenomPublicKey *dpub =
     732          81 :       wbh->with_age_proof ?
     733          81 :       wbh->blinded.with_age_proof_input[i].denom_pub :
     734          72 :       wbh->blinded.input[i].denom_pub;
     735             : 
     736          81 :     FAIL_IF (0 >
     737             :              TALER_amount_add (&wbh->amount,
     738             :                                &wbh->amount,
     739             :                                &dpub->value));
     740          81 :     FAIL_IF (0 >
     741             :              TALER_amount_add (&wbh->fee,
     742             :                                &wbh->fee,
     743             :                                &dpub->fees.withdraw));
     744             : 
     745          81 :     if (GNUNET_CRYPTO_BSA_CS ==
     746          81 :         dpub->key.bsign_pub_key->cipher)
     747          38 :       GNUNET_assert (NULL != wbh->blinding_seed);
     748             : 
     749             :   }
     750             : 
     751          75 :   if (wbh->with_age_proof || wbh->max_age > 0)
     752             :   {
     753           5 :     wbh->age_mask =
     754           5 :       wbh->blinded.with_age_proof_input[0].denom_pub->key.age_mask;
     755             : 
     756           5 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     757             :                 "Attempting to withdraw from reserve %s with maximum age %d to proof\n",
     758             :                 TALER_B2S (&wbh->reserve_pub),
     759             :                 wbh->max_age);
     760             :   }
     761             :   else
     762             :   {
     763          70 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     764             :                 "Attempting to withdraw from reserve %s\n",
     765             :                 TALER_B2S (&wbh->reserve_pub));
     766             :   }
     767             : 
     768          75 :   coins_hctx = GNUNET_CRYPTO_hash_context_start ();
     769          75 :   FAIL_IF (NULL == coins_hctx);
     770             : 
     771          75 :   j_denoms = json_array ();
     772          75 :   j_planchets = json_array ();
     773          75 :   FAIL_IF ((NULL == j_denoms) ||
     774             :            (NULL == j_planchets));
     775             : 
     776         156 :   for (size_t i  = 0; i< wbh->num_input; i++)
     777             :   {
     778             :     /* Build the denomination array */
     779          81 :     const struct TALER_EXCHANGE_DenomPublicKey *denom_pub =
     780          81 :       wbh->with_age_proof ?
     781          81 :       wbh->blinded.with_age_proof_input[i].denom_pub :
     782          72 :       wbh->blinded.input[i].denom_pub;
     783          81 :     const struct TALER_DenominationHashP *denom_h = &denom_pub->h_key;
     784             :     json_t *jdenom;
     785             : 
     786             :     /* The mask must be the same for all coins */
     787          81 :     FAIL_IF (wbh->with_age_proof &&
     788             :              (wbh->age_mask.bits != denom_pub->key.age_mask.bits));
     789             : 
     790          81 :     jdenom = GNUNET_JSON_from_data_auto (denom_h);
     791          81 :     FAIL_IF (NULL == jdenom);
     792          81 :     FAIL_IF (0 > json_array_append_new (j_denoms,
     793             :                                         jdenom));
     794             :   }
     795             : 
     796             : 
     797             :   /* Build the planchet array and calculate the hash over all planchets. */
     798          75 :   if (! wbh->with_age_proof)
     799             :   {
     800         142 :     for (size_t i  = 0; i< wbh->num_input; i++)
     801             :     {
     802          72 :       const struct TALER_PlanchetDetail *planchet =
     803          72 :         &wbh->blinded.input[i].planchet_details;
     804          72 :       json_t *jc = GNUNET_JSON_PACK (
     805             :         TALER_JSON_pack_blinded_planchet (
     806             :           NULL,
     807             :           &planchet->blinded_planchet));
     808          72 :       FAIL_IF (NULL == jc);
     809          72 :       FAIL_IF (0 > json_array_append_new (j_planchets,
     810             :                                           jc));
     811             : 
     812          72 :       TALER_coin_ev_hash (&planchet->blinded_planchet,
     813             :                           &planchet->denom_pub_hash,
     814             :                           &bch);
     815             : 
     816          72 :       GNUNET_CRYPTO_hash_context_read (coins_hctx,
     817             :                                        &bch,
     818             :                                        sizeof(bch));
     819             :     }
     820             :   }
     821             :   else
     822             :   { /* Age restricted case with required age-proof. */
     823             : 
     824             :     /**
     825             :      * We collect the run of all coin candidates for the same γ index
     826             :      * first, then γ+1 etc.
     827             :      */
     828          20 :     for (size_t k = 0; k < TALER_CNC_KAPPA; k++)
     829             :     {
     830             :       struct GNUNET_HashContext *batch_ctx;
     831             :       struct TALER_BlindedCoinHashP batch_h;
     832             : 
     833          15 :       batch_ctx = GNUNET_CRYPTO_hash_context_start ();
     834          15 :       FAIL_IF (NULL == batch_ctx);
     835             : 
     836          42 :       for (size_t i  = 0; i< wbh->num_input; i++)
     837             :       {
     838          27 :         const struct TALER_PlanchetDetail *planchet =
     839          27 :           &wbh->blinded.with_age_proof_input[i].planchet_details[k];
     840          27 :         json_t *jc = GNUNET_JSON_PACK (
     841             :           TALER_JSON_pack_blinded_planchet (
     842             :             NULL,
     843             :             &planchet->blinded_planchet));
     844             : 
     845          27 :         FAIL_IF (NULL == jc);
     846          27 :         FAIL_IF (0 > json_array_append_new (
     847             :                    j_planchets,
     848             :                    jc));
     849             : 
     850          27 :         TALER_coin_ev_hash (
     851             :           &planchet->blinded_planchet,
     852             :           &planchet->denom_pub_hash,
     853             :           &bch);
     854             : 
     855          27 :         GNUNET_CRYPTO_hash_context_read (
     856             :           batch_ctx,
     857             :           &bch,
     858             :           sizeof(bch));
     859             :       }
     860             : 
     861          15 :       GNUNET_CRYPTO_hash_context_finish (
     862             :         batch_ctx,
     863             :         &batch_h.hash);
     864          15 :       GNUNET_CRYPTO_hash_context_read (
     865             :         coins_hctx,
     866             :         &batch_h,
     867             :         sizeof(batch_h));
     868             :     }
     869             :   }
     870             : 
     871             :   /* Build the hash of the planchets */
     872          75 :   GNUNET_CRYPTO_hash_context_finish (
     873             :     coins_hctx,
     874             :     &wbh->planchets_h.hash);
     875          75 :   coins_hctx = NULL;
     876             : 
     877             :   /* Sign the request */
     878         145 :   TALER_wallet_withdraw_sign (
     879          75 :     &wbh->amount,
     880          75 :     &wbh->fee,
     881          75 :     &wbh->planchets_h,
     882             :     wbh->blinding_seed,
     883          75 :     wbh->with_age_proof ? &wbh->age_mask: NULL,
     884          75 :     wbh->with_age_proof ? wbh->max_age : 0,
     885             :     wbh->reserve_priv,
     886             :     &wbh->reserve_sig);
     887             : 
     888             :   /* Initiate the POST-request */
     889          75 :   j_request_body = GNUNET_JSON_PACK (
     890             :     GNUNET_JSON_pack_string ("cipher",
     891             :                              "ED25519"),
     892             :     GNUNET_JSON_pack_data_auto ("reserve_pub",
     893             :                                 &wbh->reserve_pub),
     894             :     GNUNET_JSON_pack_array_steal ("denoms_h",
     895             :                                   j_denoms),
     896             :     GNUNET_JSON_pack_array_steal ("coin_evs",
     897             :                                   j_planchets),
     898             :     GNUNET_JSON_pack_allow_null (
     899             :       wbh->with_age_proof
     900             :       ? GNUNET_JSON_pack_int64 ("max_age",
     901             :                                 wbh->max_age)
     902             :       : GNUNET_JSON_pack_string ("max_age",
     903             :                                  NULL) ),
     904             :     GNUNET_JSON_pack_data_auto ("reserve_sig",
     905             :                                 &wbh->reserve_sig));
     906          75 :   FAIL_IF (NULL == j_request_body);
     907             : 
     908          75 :   if (NULL != wbh->blinding_seed)
     909             :   {
     910          35 :     json_t *j_seed = GNUNET_JSON_PACK (
     911             :       GNUNET_JSON_pack_data_auto ("blinding_seed",
     912             :                                   wbh->blinding_seed));
     913          35 :     GNUNET_assert (NULL != j_seed);
     914          35 :     GNUNET_assert (0 ==
     915             :                    json_object_update_new (
     916             :                      j_request_body,
     917             :                      j_seed));
     918             :   }
     919             : 
     920          75 :   curlh = TALER_EXCHANGE_curl_easy_get_ (wbh->request_url);
     921          75 :   FAIL_IF (NULL == curlh);
     922          75 :   FAIL_IF (GNUNET_OK !=
     923             :            TALER_curl_easy_post (
     924             :              &wbh->post_ctx,
     925             :              curlh,
     926             :              j_request_body));
     927          75 :   json_decref (j_request_body);
     928          75 :   j_request_body = NULL;
     929             : 
     930         150 :   wbh->job = GNUNET_CURL_job_add2 (
     931             :     wbh->curl_ctx,
     932             :     curlh,
     933          75 :     wbh->post_ctx.headers,
     934             :     &handle_withdraw_blinded_finished,
     935             :     wbh);
     936          75 :   FAIL_IF (NULL == wbh->job);
     937             : 
     938             :   /* No errors, return */
     939          75 :   return;
     940             : 
     941           0 : ERROR:
     942           0 :   if (NULL != coins_hctx)
     943           0 :     GNUNET_CRYPTO_hash_context_abort (coins_hctx);
     944           0 :   if (NULL != j_denoms)
     945           0 :     json_decref (j_denoms);
     946           0 :   if (NULL != j_planchets)
     947           0 :     json_decref (j_planchets);
     948           0 :   if (NULL != j_request_body)
     949           0 :     json_decref (j_request_body);
     950           0 :   if (NULL != curlh)
     951           0 :     curl_easy_cleanup (curlh);
     952           0 :   TALER_EXCHANGE_withdraw_blinded_cancel (wbh);
     953           0 :   return;
     954             : #undef FAIL_IF
     955             : }
     956             : 
     957             : 
     958             : /**
     959             :  * @brief Callback to copy the results from the call to TALER_withdraw_blinded
     960             :  * in the non-age-restricted case to the result for the originating call from TALER_withdraw.
     961             :  *
     962             :  * @param cls struct TALER_WithdrawHandle
     963             :  * @param wbr The response
     964             :  */
     965             : static void
     966          70 : copy_results (
     967             :   void *cls,
     968             :   const struct TALER_EXCHANGE_WithdrawBlindedResponse *wbr)
     969             : {
     970             :   /* The original handle from the top-level call to withdraw */
     971          70 :   struct TALER_EXCHANGE_WithdrawHandle *wh = cls;
     972          70 :   struct TALER_EXCHANGE_WithdrawResponse resp = {
     973             :     .hr = wbr->hr,
     974             :   };
     975             : 
     976          70 :   wh->withdraw_blinded_handle = NULL;
     977             : 
     978             :   /**
     979             :    * The withdraw protocol has been performed with blinded data.
     980             :    * Now the response can be copied as is, except for the MHD_HTTP_OK case,
     981             :    * in which we now need to perform the unblinding.
     982             :    */
     983          70 :   switch (wbr->hr.http_status)
     984             :   {
     985          61 :   case MHD_HTTP_OK:
     986          61 :     {
     987             :       struct TALER_EXCHANGE_WithdrawCoinPrivateDetails
     988          61 :         details[GNUNET_NZL (wh->num_coins)];
     989          61 :       bool ok = true;
     990             : 
     991          61 :       GNUNET_assert (wh->num_coins == wbr->details.ok.num_sigs);
     992          61 :       memset (details,
     993             :               0,
     994             :               sizeof(details));
     995          61 :       resp.details.ok.num_sigs = wbr->details.ok.num_sigs;
     996          61 :       resp.details.ok.coin_details = details;
     997          61 :       resp.details.ok.planchets_h = wbr->details.ok.planchets_h;
     998         124 :       for (size_t n = 0; n<wh->num_coins; n++)
     999             :       {
    1000          63 :         const struct TALER_BlindedDenominationSignature *bsig =
    1001          63 :           &wbr->details.ok.blinded_denom_sigs[n];
    1002          63 :         struct CoinData *cd = &wh->coin_data[n];
    1003          63 :         struct TALER_EXCHANGE_WithdrawCoinPrivateDetails *coin = &details[n];
    1004             :         struct TALER_FreshCoin fresh_coin;
    1005             : 
    1006          63 :         *coin = wh->coin_data[n].candidates[0].details;
    1007          63 :         coin->planchet = wh->coin_data[n].planchet_details[0];
    1008          63 :         GNUNET_CRYPTO_eddsa_key_get_public (
    1009          63 :           &coin->coin_priv.eddsa_priv,
    1010             :           &coin->coin_pub.eddsa_pub);
    1011             : 
    1012          63 :         if (GNUNET_OK !=
    1013          63 :             TALER_planchet_to_coin (&cd->denom_pub.key,
    1014             :                                     bsig,
    1015          63 :                                     &coin->blinding_key,
    1016          63 :                                     &coin->coin_priv,
    1017          63 :                                     &coin->h_age_commitment,
    1018          63 :                                     &coin->h_coin_pub,
    1019          63 :                                     &coin->blinding_values,
    1020             :                                     &fresh_coin))
    1021             :         {
    1022           0 :           resp.hr.http_status = 0;
    1023           0 :           resp.hr.ec = TALER_EC_EXCHANGE_WITHDRAW_UNBLIND_FAILURE;
    1024           0 :           GNUNET_break_op (0);
    1025           0 :           ok = false;
    1026           0 :           break;
    1027             :         }
    1028          63 :         coin->denom_sig = fresh_coin.sig;
    1029             :       }
    1030          61 :       if (ok)
    1031             :       {
    1032          61 :         wh->callback (
    1033             :           wh->callback_cls,
    1034             :           &resp);
    1035          61 :         wh->callback = NULL;
    1036             :       }
    1037         124 :       for (size_t n = 0; n<wh->num_coins; n++)
    1038             :       {
    1039          63 :         struct TALER_EXCHANGE_WithdrawCoinPrivateDetails *coin = &details[n];
    1040             : 
    1041          63 :         TALER_denom_sig_free (&coin->denom_sig);
    1042             :       }
    1043          61 :       break;
    1044             :     }
    1045           0 :   case MHD_HTTP_CREATED:
    1046           0 :     resp.details.created  = wbr->details.created;
    1047           0 :     break;
    1048             : 
    1049           4 :   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
    1050           4 :     resp.details.unavailable_for_legal_reasons =
    1051             :       wbr->details.unavailable_for_legal_reasons;
    1052           4 :     break;
    1053             : 
    1054           5 :   default:
    1055             :     /* nothing to do here, .hr.ec and .hr.hint are all set already from previous response */
    1056           5 :     break;
    1057             :   }
    1058          70 :   if (NULL != wh->callback)
    1059             :   {
    1060           9 :     wh->callback (
    1061             :       wh->callback_cls,
    1062             :       &resp);
    1063           9 :     wh->callback = NULL;
    1064             :   }
    1065          70 :   TALER_EXCHANGE_withdraw_cancel (wh);
    1066          70 : }
    1067             : 
    1068             : 
    1069             : /**
    1070             :  * @brief Callback to copy the results from the call to TALER_withdraw_blinded
    1071             :  * in the age-restricted case to the result for the originating call from TALER_withdraw.
    1072             :  *
    1073             :  * @param cls struct TALER_WithdrawHandle
    1074             :  * @param wbr The response
    1075             :  */
    1076             : static void
    1077           5 : copy_results_with_age_proof (
    1078             :   void *cls,
    1079             :   const struct TALER_EXCHANGE_WithdrawBlindedResponse *wbr)
    1080           5 : {
    1081             :   /* The original handle from the top-level call to withdraw */
    1082           5 :   struct TALER_EXCHANGE_WithdrawHandle *wh = cls;
    1083           5 :   uint8_t k =  wbr->details.created.noreveal_index;
    1084           5 :   struct TALER_EXCHANGE_WithdrawCoinPrivateDetails details[wh->num_coins];
    1085           5 :   struct TALER_EXCHANGE_WithdrawResponse resp = {
    1086             :     .hr = wbr->hr,
    1087             :   };
    1088             : 
    1089           5 :   wh->withdraw_blinded_handle = NULL;
    1090             : 
    1091           5 :   switch (wbr->hr.http_status)
    1092             :   {
    1093           0 :   case MHD_HTTP_OK:
    1094             :     /* in the age-restricted case, this should not happen */
    1095           0 :     GNUNET_break_op (0);
    1096           0 :     break;
    1097             : 
    1098           0 :   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
    1099           0 :     resp.details.unavailable_for_legal_reasons =
    1100             :       wbr->details.unavailable_for_legal_reasons;
    1101           0 :     break;
    1102             : 
    1103           3 :   case MHD_HTTP_CREATED:
    1104             :     {
    1105           3 :       GNUNET_assert (wh->num_coins == wbr->details.created.num_coins);
    1106           3 :       resp.details.created = wbr->details.created;
    1107           3 :       resp.details.created.coin_details = details;
    1108           3 :       resp.details.created.kappa_seed = wh->kappa_seed;
    1109           3 :       memset (details,
    1110             :               0,
    1111             :               sizeof(details));
    1112          10 :       for (size_t n = 0; n< wh->num_coins; n++)
    1113             :       {
    1114           7 :         details[n] = wh->coin_data[n].candidates[k].details;
    1115           7 :         details[n].planchet = wh->coin_data[n].planchet_details[k];
    1116             :       }
    1117           3 :       break;
    1118             :     }
    1119             : 
    1120           2 :   default:
    1121           2 :     break;
    1122             :   }
    1123             : 
    1124           5 :   wh->callback (
    1125             :     wh->callback_cls,
    1126             :     &resp);
    1127           5 :   wh->callback = NULL;
    1128           5 :   TALER_EXCHANGE_withdraw_cancel (wh);
    1129           5 : }
    1130             : 
    1131             : 
    1132             : /**
    1133             :  * @brief Prepares and executes TALER_EXCHANGE_withdraw_blinded.
    1134             :  * If there were CS-denominations involved, started once the all calls
    1135             :  * to /blinding-prepare are done.
    1136             :  */
    1137             : static void
    1138          75 : call_withdraw_blinded (
    1139             :   struct TALER_EXCHANGE_WithdrawHandle *wh)
    1140             : {
    1141             : 
    1142          75 :   GNUNET_assert (NULL == wh->blinding_prepare_handle);
    1143             : 
    1144          75 :   if (! wh->with_age_proof)
    1145          70 :   {
    1146          70 :     struct TALER_EXCHANGE_WithdrawBlindedCoinInput input[wh->num_coins];
    1147             : 
    1148          70 :     memset (input,
    1149             :             0,
    1150             :             sizeof(input));
    1151             : 
    1152             :     /* Prepare the blinded planchets as input */
    1153         142 :     for (size_t n = 0; n < wh->num_coins; n++)
    1154             :     {
    1155          72 :       input[n].denom_pub =
    1156          72 :         &wh->coin_data[n].denom_pub;
    1157          72 :       input[n].planchet_details =
    1158          72 :         *wh->coin_data[n].planchet_details;
    1159             :     }
    1160             : 
    1161          70 :     wh->withdraw_blinded_handle =
    1162          70 :       TALER_EXCHANGE_withdraw_blinded (
    1163             :         wh->curl_ctx,
    1164             :         wh->keys,
    1165             :         wh->exchange_url,
    1166             :         wh->reserve_priv,
    1167          70 :         wh->has_blinding_seed ? &wh->blinding_seed : NULL,
    1168             :         wh->num_coins,
    1169             :         input,
    1170             :         &copy_results,
    1171             :         wh);
    1172             :   }
    1173             :   else
    1174           5 :   {  /* age restricted case */
    1175             :     struct TALER_EXCHANGE_WithdrawBlindedAgeRestrictedCoinInput
    1176           5 :       ari[wh->num_coins];
    1177             : 
    1178           5 :     memset (ari,
    1179             :             0,
    1180             :             sizeof(ari));
    1181             : 
    1182             :     /* Prepare the blinded planchets as input */
    1183          14 :     for (size_t n = 0; n < wh->num_coins; n++)
    1184             :     {
    1185           9 :       ari[n].denom_pub = &wh->coin_data[n].denom_pub;
    1186          36 :       for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
    1187          27 :         ari[n].planchet_details[k] =
    1188          27 :           wh->coin_data[n].planchet_details[k];
    1189             :     }
    1190             : 
    1191           5 :     wh->withdraw_blinded_handle =
    1192           5 :       TALER_EXCHANGE_withdraw_blinded_with_age_proof (
    1193             :         wh->curl_ctx,
    1194             :         wh->keys,
    1195             :         wh->exchange_url,
    1196             :         wh->reserve_priv,
    1197           5 :         wh->has_blinding_seed ? &wh->blinding_seed : NULL,
    1198           5 :         wh->max_age,
    1199           5 :         wh->num_coins,
    1200             :         ari,
    1201             :         &copy_results_with_age_proof,
    1202             :         wh);
    1203             :   }
    1204          75 : }
    1205             : 
    1206             : 
    1207             : /**
    1208             :  * @brief Function called when /blinding-prepare is finished
    1209             :  *
    1210             :  * @param cls the `struct BlindingPrepareClosure *`
    1211             :  * @param bpr replies from the /blinding-prepare request
    1212             :  */
    1213             : static void
    1214          35 : blinding_prepare_done (
    1215             :   void *cls,
    1216             :   const struct TALER_EXCHANGE_BlindingPrepareResponse *bpr)
    1217             : {
    1218          35 :   struct BlindingPrepareClosure *bpcls = cls;
    1219          35 :   struct TALER_EXCHANGE_WithdrawHandle *wh = bpcls->withdraw_handle;
    1220             : 
    1221          35 :   wh->blinding_prepare_handle = NULL;
    1222          35 :   switch (bpr->hr.http_status)
    1223             :   {
    1224          35 :   case MHD_HTTP_OK:
    1225             :     {
    1226          35 :       bool success = false;
    1227          35 :       size_t num = bpr->details.ok.num_blinding_values;
    1228             : 
    1229          35 :       GNUNET_assert (0 != num);
    1230          35 :       GNUNET_assert (num == bpcls->num_nonces);
    1231          81 :       for (size_t i = 0; i < bpcls->num_prepare_coins; i++)
    1232             :       {
    1233          46 :         struct TALER_PlanchetDetail *planchet = bpcls->coins[i].planchet;
    1234          46 :         struct CoinCandidate *can = bpcls->coins[i].candidate;
    1235          46 :         size_t cs_idx = bpcls->coins[i].cs_idx;
    1236             : 
    1237          46 :         GNUNET_assert (NULL != can);
    1238          46 :         GNUNET_assert (NULL != planchet);
    1239          46 :         success = false;
    1240             : 
    1241             :         /* Complete the initialization of the coin with CS denomination */
    1242          46 :         TALER_denom_ewv_copy (
    1243             :           &can->details.blinding_values,
    1244          46 :           &bpr->details.ok.blinding_values[cs_idx]);
    1245             : 
    1246          46 :         GNUNET_assert (GNUNET_CRYPTO_BSA_CS ==
    1247             :                        can->details.blinding_values.blinding_inputs->cipher);
    1248             : 
    1249          46 :         TALER_planchet_setup_coin_priv (
    1250          46 :           &can->details.secret,
    1251          46 :           &can->details.blinding_values,
    1252             :           &can->details.coin_priv);
    1253             : 
    1254          46 :         TALER_planchet_blinding_secret_create (
    1255          46 :           &can->details.secret,
    1256          46 :           &can->details.blinding_values,
    1257             :           &can->details.blinding_key);
    1258             : 
    1259             :         /* This initializes the 2nd half of the
    1260             :            can->planchet_detail.blinded_planchet */
    1261          46 :         if (GNUNET_OK !=
    1262          46 :             TALER_planchet_prepare (
    1263          46 :               bpcls->coins[i].denom_pub,
    1264          46 :               &can->details.blinding_values,
    1265          46 :               &can->details.blinding_key,
    1266          46 :               &bpcls->nonces[cs_idx],
    1267          46 :               &can->details.coin_priv,
    1268          46 :               &can->details.h_age_commitment,
    1269             :               &can->details.h_coin_pub,
    1270             :               planchet))
    1271             :         {
    1272           0 :           GNUNET_break (0);
    1273           0 :           break;
    1274             :         }
    1275             : 
    1276          46 :         TALER_coin_ev_hash (&planchet->blinded_planchet,
    1277          46 :                             &planchet->denom_pub_hash,
    1278             :                             &can->blinded_coin_h);
    1279          46 :         success = true;
    1280             :       }
    1281             : 
    1282             :       /* /blinding-prepare is done, we can now perform the
    1283             :        * actual withdraw operation */
    1284          35 :       if (success)
    1285          35 :         call_withdraw_blinded (wh);
    1286          35 :       goto cleanup;
    1287             :     }
    1288           0 :   default:
    1289             :     {
    1290             :       /* We got an error condition during blinding prepare that we need to report */
    1291           0 :       struct TALER_EXCHANGE_WithdrawResponse resp = {
    1292             :         .hr = bpr->hr
    1293             :       };
    1294             : 
    1295           0 :       wh->callback (
    1296             :         wh->callback_cls,
    1297             :         &resp);
    1298             : 
    1299           0 :       wh->callback = NULL;
    1300           0 :       break;
    1301             :     }
    1302             :   }
    1303           0 :   TALER_EXCHANGE_withdraw_cancel (wh);
    1304          35 : cleanup:
    1305          35 :   GNUNET_free (bpcls->coins);
    1306          35 :   GNUNET_free (bpcls->nonces);
    1307          35 :   GNUNET_free (bpcls);
    1308          35 : }
    1309             : 
    1310             : 
    1311             : /**
    1312             :  * @brief Prepares non age-restricted coins for the call to withdraw and
    1313             :  * calculates the total amount with fees.
    1314             :  * For denomination with CS as cipher, initiates the preflight to retrieve the
    1315             :  * bpcls-parameter via /blinding-prepare.
    1316             :  * Note that only one of the three parameters seed, tuples or secrets must not be NULL
    1317             :  *
    1318             :  * @param wh The handler to the withdraw
    1319             :  * @param num_coins Number of coins to withdraw
    1320             :  * @param max_age The maximum age to commit to
    1321             :  * @param denoms_pub Array @e num_coins of denominations
    1322             :  * @param seed master seed from which to derive @e num_coins secrets and blinding, if @e blinding_seed is NULL
    1323             :  * @param blinding_seed master seed for the blinding. Might be NULL, in which case the blinding_seed is derived from @e seed
    1324             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
    1325             :  */
    1326             : static enum GNUNET_GenericReturnValue
    1327          75 : prepare_coins (
    1328             :   struct TALER_EXCHANGE_WithdrawHandle *wh,
    1329             :   size_t num_coins,
    1330             :   uint8_t max_age,
    1331             :   const struct TALER_EXCHANGE_DenomPublicKey *denoms_pub,
    1332             :   const struct TALER_WithdrawMasterSeedP *seed,
    1333             :   const struct TALER_BlindingMasterSeedP *blinding_seed)
    1334             : {
    1335          75 :   size_t cs_num = 0;
    1336             :   struct BlindingPrepareClosure *cs_closure;
    1337             :   uint8_t kappa;
    1338             : 
    1339             : #define FAIL_IF(cond) \
    1340             :         do \
    1341             :         { \
    1342             :           if ((cond)) \
    1343             :           { \
    1344             :             GNUNET_break (! (cond)); \
    1345             :             goto ERROR; \
    1346             :           } \
    1347             :         } while (0)
    1348             : 
    1349          75 :   GNUNET_assert (0 < num_coins);
    1350             : 
    1351          75 :   wh->num_coins = num_coins;
    1352          75 :   wh->max_age = max_age;
    1353          75 :   wh->age_mask = denoms_pub[0].key.age_mask;
    1354          75 :   wh->coin_data = GNUNET_new_array (
    1355             :     wh->num_coins,
    1356             :     struct CoinData);
    1357             : 
    1358             :   /* First, figure out how many Clause-Schnorr denominations we have */
    1359         156 :   for (size_t i =0; i< wh->num_coins; i++)
    1360             :   {
    1361          81 :     if (GNUNET_CRYPTO_BSA_CS ==
    1362          81 :         denoms_pub[i].key.bsign_pub_key->cipher)
    1363          38 :       cs_num++;
    1364             :   }
    1365             : 
    1366          75 :   if (wh->with_age_proof)
    1367             :   {
    1368           5 :     kappa = TALER_CNC_KAPPA;
    1369             :   }
    1370             :   else
    1371             :   {
    1372          70 :     kappa = 1;
    1373             :   }
    1374             : 
    1375          75 :   {
    1376          75 :     struct TALER_PlanchetMasterSecretP secrets[kappa][num_coins];
    1377          75 :     struct TALER_EXCHANGE_NonceKey cs_nonce_keys[GNUNET_NZL (cs_num)];
    1378          75 :     uint32_t cs_indices[GNUNET_NZL (cs_num)];
    1379             : 
    1380          75 :     size_t cs_denom_idx = 0;
    1381          75 :     size_t cs_coin_idx = 0;
    1382             : 
    1383          75 :     if (wh->with_age_proof)
    1384             :     {
    1385           5 :       TALER_withdraw_expand_kappa_seed (seed,
    1386             :                                         &wh->kappa_seed);
    1387          20 :       for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
    1388             :       {
    1389          15 :         TALER_withdraw_expand_secrets (
    1390             :           num_coins,
    1391          15 :           &wh->kappa_seed.tuple[k],
    1392          15 :           secrets[k]);
    1393             :       }
    1394             :     }
    1395             :     else
    1396             :     {
    1397          70 :       TALER_withdraw_expand_secrets (
    1398             :         num_coins,
    1399             :         seed,
    1400          70 :         secrets[0]);
    1401             :     }
    1402             : 
    1403          75 :     if (0 < cs_num)
    1404             :     {
    1405          35 :       memset (cs_nonce_keys,
    1406             :               0,
    1407             :               sizeof(cs_nonce_keys));
    1408          35 :       cs_closure = GNUNET_new (struct BlindingPrepareClosure);
    1409          35 :       cs_closure->withdraw_handle = wh;
    1410          35 :       cs_closure->num_prepare_coins = cs_num * kappa;
    1411          35 :       GNUNET_assert ((1 == kappa) || (cs_num * kappa > cs_num));
    1412          35 :       cs_closure->coins =
    1413          35 :         GNUNET_new_array (cs_closure->num_prepare_coins,
    1414             :                           struct BlindingPrepareCoinData);
    1415          35 :       cs_closure->num_nonces = cs_num;
    1416          35 :       cs_closure->nonces =
    1417          35 :         GNUNET_new_array (cs_closure->num_nonces,
    1418             :                           union GNUNET_CRYPTO_BlindSessionNonce);
    1419             :     }
    1420             :     else
    1421             :     {
    1422          40 :       cs_closure = NULL;
    1423             :     }
    1424             : 
    1425         156 :     for (uint32_t i = 0; i < wh->num_coins; i++)
    1426             :     {
    1427          81 :       struct CoinData *cd = &wh->coin_data[i];
    1428          81 :       bool age_denom = (0 != denoms_pub[i].key.age_mask.bits);
    1429             : 
    1430          81 :       cd->denom_pub = denoms_pub[i];
    1431             :       /* The age mask must be the same for all coins */
    1432          81 :       FAIL_IF (wh->with_age_proof &&
    1433             :                (0 ==  denoms_pub[i].key.age_mask.bits));
    1434          81 :       FAIL_IF (wh->age_mask.bits !=
    1435             :                denoms_pub[i].key.age_mask.bits);
    1436          81 :       TALER_denom_pub_copy (&cd->denom_pub.key,
    1437          81 :                             &denoms_pub[i].key);
    1438             : 
    1439             :       /* Mark the indices of the coins which are of type Clause-Schnorr
    1440             :        * and add their denomination public key hash to the list.
    1441             :        */
    1442          81 :       if (GNUNET_CRYPTO_BSA_CS ==
    1443          81 :           cd->denom_pub.key.bsign_pub_key->cipher)
    1444             :       {
    1445          38 :         GNUNET_assert (cs_denom_idx<cs_num);
    1446          38 :         cs_indices[cs_denom_idx] = i;
    1447          38 :         cs_nonce_keys[cs_denom_idx].cnc_num = i;
    1448          38 :         cs_nonce_keys[cs_denom_idx].pk = &cd->denom_pub;
    1449          38 :         cs_denom_idx++;
    1450             :       }
    1451             : 
    1452             :       /*
    1453             :        * Note that we "loop" here either only once (if with_age_proof is false),
    1454             :        * or TALER_CNC_KAPPA times.
    1455             :        */
    1456         180 :       for (uint8_t k = 0; k < kappa; k++)
    1457             :       {
    1458          99 :         struct CoinCandidate *can = &cd->candidates[k];
    1459          99 :         struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k];
    1460             : 
    1461          99 :         can->details.secret = secrets[k][i];
    1462             :         /*
    1463             :          * The age restriction needs to be set on a coin if the denomination
    1464             :          * support age restriction. Note that his is regardless of weither
    1465             :          *  with_age_proof is set or not.
    1466             :          */
    1467          99 :         if (age_denom)
    1468             :         {
    1469             :           /* Derive the age restriction from the given secret and
    1470             :            * the maximum age */
    1471          37 :           TALER_age_restriction_from_secret (
    1472          37 :             &can->details.secret,
    1473          37 :             &wh->age_mask,
    1474          37 :             wh->max_age,
    1475             :             &can->details.age_commitment_proof);
    1476             : 
    1477          37 :           TALER_age_commitment_hash (
    1478          37 :             &can->details.age_commitment_proof.commitment,
    1479             :             &can->details.h_age_commitment);
    1480             :         }
    1481             : 
    1482          99 :         switch (cd->denom_pub.key.bsign_pub_key->cipher)
    1483             :         {
    1484          53 :         case GNUNET_CRYPTO_BSA_RSA:
    1485          53 :           TALER_denom_ewv_copy (&can->details.blinding_values,
    1486             :                                 TALER_denom_ewv_rsa_singleton ());
    1487          53 :           TALER_planchet_setup_coin_priv (&can->details.secret,
    1488          53 :                                           &can->details.blinding_values,
    1489             :                                           &can->details.coin_priv);
    1490          53 :           TALER_planchet_blinding_secret_create (&can->details.secret,
    1491          53 :                                                  &can->details.blinding_values,
    1492             :                                                  &can->details.blinding_key);
    1493          53 :           FAIL_IF (GNUNET_OK !=
    1494             :                    TALER_planchet_prepare (&cd->denom_pub.key,
    1495             :                                            &can->details.blinding_values,
    1496             :                                            &can->details.blinding_key,
    1497             :                                            NULL,
    1498             :                                            &can->details.coin_priv,
    1499             :                                            (age_denom)
    1500             :                                            ? &can->details.h_age_commitment
    1501             :                                            : NULL,
    1502             :                                            &can->details.h_coin_pub,
    1503             :                                            planchet));
    1504          53 :           TALER_coin_ev_hash (&planchet->blinded_planchet,
    1505          53 :                               &planchet->denom_pub_hash,
    1506             :                               &can->blinded_coin_h);
    1507             : 
    1508          53 :           break;
    1509             : 
    1510          46 :         case GNUNET_CRYPTO_BSA_CS:
    1511             :           {
    1512             :             /**
    1513             :              * Prepare the nonce and save the index and the denomination for the callback
    1514             :              * after the call to blinding-prepare
    1515             :              */
    1516          46 :             cs_closure->coins[cs_coin_idx].candidate = can;
    1517          46 :             cs_closure->coins[cs_coin_idx].planchet = planchet;
    1518          46 :             cs_closure->coins[cs_coin_idx].denom_pub = &cd->denom_pub.key;
    1519          46 :             cs_closure->coins[cs_coin_idx].cs_idx = i;
    1520          46 :             cs_closure->coins[cs_coin_idx].age_denom = age_denom;
    1521          46 :             cs_coin_idx++;
    1522          46 :             break;
    1523             :           }
    1524           0 :         default:
    1525           0 :           FAIL_IF (1);
    1526             :         }
    1527             :       }
    1528             :     }
    1529             : 
    1530          75 :     if (0 < cs_num)
    1531             :     {
    1532          35 :       if (NULL != blinding_seed)
    1533             :       {
    1534          32 :         wh->blinding_seed = *blinding_seed;
    1535             :       }
    1536             :       else
    1537             :       {
    1538           3 :         TALER_cs_withdraw_seed_to_blinding_seed (
    1539             :           seed,
    1540             :           &wh->blinding_seed);
    1541             :       }
    1542          35 :       wh->has_blinding_seed = true;
    1543             : 
    1544          35 :       TALER_cs_derive_only_cs_blind_nonces_from_seed (
    1545          35 :         &wh->blinding_seed,
    1546             :         false, /* not for melt */
    1547             :         cs_num,
    1548             :         cs_indices,
    1549             :         cs_closure->nonces);
    1550             : 
    1551          35 :       wh->blinding_prepare_handle =
    1552          35 :         TALER_EXCHANGE_blinding_prepare_for_withdraw (
    1553             :           wh->curl_ctx,
    1554             :           wh->exchange_url,
    1555             :           &wh->blinding_seed,
    1556             :           cs_num,
    1557             :           cs_nonce_keys,
    1558             :           &blinding_prepare_done,
    1559             :           cs_closure);
    1560          35 :       FAIL_IF (NULL == wh->blinding_prepare_handle);
    1561             :     }
    1562             :   }
    1563          75 :   return GNUNET_OK;
    1564             : 
    1565           0 : ERROR:
    1566           0 :   if (0<cs_num)
    1567             :   {
    1568           0 :     GNUNET_free (cs_closure->nonces);
    1569           0 :     GNUNET_free (cs_closure);
    1570             :   }
    1571           0 :   TALER_EXCHANGE_withdraw_cancel (wh);
    1572           0 :   return GNUNET_SYSERR;
    1573             : #undef FAIL_IF
    1574             : 
    1575             : }
    1576             : 
    1577             : 
    1578             : /**
    1579             :  * Prepare a withdraw handle for both, the non-restricted
    1580             :  * and age-restricted case.
    1581             :  *
    1582             :  * @param curl_ctx The curl context to use
    1583             :  * @param keys The keys from the exchange
    1584             :  * @param exchange_url The base url to the exchange
    1585             :  * @param reserve_priv The private key of the exchange
    1586             :  * @param res_cb The callback to call on response
    1587             :  * @param res_cb_cls The closure to pass to the callback
    1588             :  */
    1589             : static struct TALER_EXCHANGE_WithdrawHandle *
    1590          75 : setup_withdraw_handle (
    1591             :   struct GNUNET_CURL_Context *curl_ctx,
    1592             :   struct TALER_EXCHANGE_Keys *keys,
    1593             :   const char *exchange_url,
    1594             :   const struct TALER_ReservePrivateKeyP *reserve_priv,
    1595             :   TALER_EXCHANGE_WithdrawCallback res_cb,
    1596             :   void *res_cb_cls)
    1597             : {
    1598             :   struct TALER_EXCHANGE_WithdrawHandle *wh;
    1599             : 
    1600          75 :   wh = GNUNET_new (struct TALER_EXCHANGE_WithdrawHandle);
    1601          75 :   wh->exchange_url = exchange_url;
    1602          75 :   wh->keys = TALER_EXCHANGE_keys_incref (keys);
    1603          75 :   wh->curl_ctx = curl_ctx;
    1604          75 :   wh->reserve_priv = reserve_priv;
    1605          75 :   wh->callback = res_cb;
    1606          75 :   wh->callback_cls = res_cb_cls;
    1607             : 
    1608          75 :   return wh;
    1609             : }
    1610             : 
    1611             : 
    1612             : struct TALER_EXCHANGE_WithdrawHandle *
    1613          70 : TALER_EXCHANGE_withdraw_extra_blinding_seed (
    1614             :   struct GNUNET_CURL_Context *curl_ctx,
    1615             :   struct TALER_EXCHANGE_Keys *keys,
    1616             :   const char *exchange_url,
    1617             :   const struct TALER_ReservePrivateKeyP *reserve_priv,
    1618             :   size_t num_coins,
    1619             :   const struct TALER_EXCHANGE_DenomPublicKey denoms_pub[static num_coins],
    1620             :   const struct TALER_WithdrawMasterSeedP *seed,
    1621             :   const struct TALER_BlindingMasterSeedP *blinding_seed,
    1622             :   uint8_t opaque_max_age,
    1623             :   TALER_EXCHANGE_WithdrawCallback res_cb,
    1624             :   void *res_cb_cls)
    1625          70 : {
    1626             :   struct TALER_EXCHANGE_WithdrawHandle *wh;
    1627             : 
    1628          70 :   wh = setup_withdraw_handle (curl_ctx,
    1629             :                               keys,
    1630             :                               exchange_url,
    1631             :                               reserve_priv,
    1632             :                               res_cb,
    1633             :                               res_cb_cls);
    1634          70 :   GNUNET_assert (NULL != wh);
    1635          70 :   wh->with_age_proof = false;
    1636             : 
    1637          70 :   if (GNUNET_OK !=
    1638          70 :       prepare_coins (wh,
    1639             :                      num_coins,
    1640             :                      opaque_max_age,
    1641             :                      denoms_pub,
    1642             :                      seed,
    1643             :                      blinding_seed))
    1644             :   {
    1645           0 :     GNUNET_free (wh);
    1646           0 :     return NULL;
    1647             :   }
    1648             : 
    1649             :   /* If there were no CS denominations, we can now perform the actual
    1650             :    * withdraw protocol.  Otherwise, there are calls to /blinding-prepare
    1651             :    * in flight and once they finish, the withdraw-protocol will be
    1652             :    * called from within the blinding_prepare_done-function.
    1653             :    */
    1654          70 :   if (NULL == wh->blinding_prepare_handle)
    1655          37 :     call_withdraw_blinded (wh);
    1656             : 
    1657          70 :   return wh;
    1658             : }
    1659             : 
    1660             : 
    1661             : struct TALER_EXCHANGE_WithdrawHandle *
    1662           2 : TALER_EXCHANGE_withdraw (
    1663             :   struct GNUNET_CURL_Context *curl_ctx,
    1664             :   struct TALER_EXCHANGE_Keys *keys,
    1665             :   const char *exchange_url,
    1666             :   const struct TALER_ReservePrivateKeyP *reserve_priv,
    1667             :   size_t num_coins,
    1668             :   const struct TALER_EXCHANGE_DenomPublicKey denoms_pub[static num_coins],
    1669             :   const struct TALER_WithdrawMasterSeedP *seed,
    1670             :   uint8_t opaque_max_age,
    1671             :   TALER_EXCHANGE_WithdrawCallback res_cb,
    1672             :   void *res_cb_cls)
    1673           2 : {
    1674           2 :   return TALER_EXCHANGE_withdraw_extra_blinding_seed (
    1675             :     curl_ctx,
    1676             :     keys,
    1677             :     exchange_url,
    1678             :     reserve_priv,
    1679             :     num_coins,
    1680             :     denoms_pub,
    1681             :     seed,
    1682             :     NULL,
    1683             :     opaque_max_age,
    1684             :     res_cb,
    1685             :     res_cb_cls
    1686             :     );
    1687             : }
    1688             : 
    1689             : 
    1690             : struct TALER_EXCHANGE_WithdrawHandle *
    1691           5 : TALER_EXCHANGE_withdraw_with_age_proof_extra_blinding_seed (
    1692             :   struct GNUNET_CURL_Context *curl_ctx,
    1693             :   struct TALER_EXCHANGE_Keys *keys,
    1694             :   const char *exchange_url,
    1695             :   const struct TALER_ReservePrivateKeyP *reserve_priv,
    1696             :   size_t num_coins,
    1697             :   const struct TALER_EXCHANGE_DenomPublicKey denoms_pub[static num_coins],
    1698             :   const struct TALER_WithdrawMasterSeedP *seed,
    1699             :   const struct TALER_BlindingMasterSeedP *blinding_seed,
    1700             :   uint8_t max_age,
    1701             :   TALER_EXCHANGE_WithdrawCallback res_cb,
    1702             :   void *res_cb_cls)
    1703           5 : {
    1704             :   struct TALER_EXCHANGE_WithdrawHandle *wh;
    1705             : 
    1706           5 :   wh = setup_withdraw_handle (curl_ctx,
    1707             :                               keys,
    1708             :                               exchange_url,
    1709             :                               reserve_priv,
    1710             :                               res_cb,
    1711             :                               res_cb_cls);
    1712           5 :   GNUNET_assert (NULL != wh);
    1713             : 
    1714           5 :   wh->with_age_proof = true;
    1715             : 
    1716           5 :   if (GNUNET_OK !=
    1717           5 :       prepare_coins (wh,
    1718             :                      num_coins,
    1719             :                      max_age,
    1720             :                      denoms_pub,
    1721             :                      seed,
    1722             :                      blinding_seed))
    1723             :   {
    1724           0 :     GNUNET_free (wh);
    1725           0 :     return NULL;
    1726             :   }
    1727             : 
    1728             :   /* If there were no CS denominations, we can now perform the actual
    1729             :    * withdraw protocol.  Otherwise, there are calls to /blinding-prepare
    1730             :    * in flight and once they finish, the withdraw-protocol will be
    1731             :    * called from within the blinding_prepare_done-function.
    1732             :    */
    1733           5 :   if (NULL == wh->blinding_prepare_handle)
    1734           3 :     call_withdraw_blinded (wh);
    1735             : 
    1736           5 :   return wh;
    1737             : }
    1738             : 
    1739             : 
    1740             : struct TALER_EXCHANGE_WithdrawHandle *
    1741           5 : TALER_EXCHANGE_withdraw_with_age_proof (
    1742             :   struct GNUNET_CURL_Context *curl_ctx,
    1743             :   struct TALER_EXCHANGE_Keys *keys,
    1744             :   const char *exchange_url,
    1745             :   const struct TALER_ReservePrivateKeyP *reserve_priv,
    1746             :   size_t num_coins,
    1747             :   const struct TALER_EXCHANGE_DenomPublicKey denoms_pub[static num_coins],
    1748             :   const struct TALER_WithdrawMasterSeedP *seed,
    1749             :   uint8_t max_age,
    1750             :   TALER_EXCHANGE_WithdrawCallback res_cb,
    1751             :   void *res_cb_cls)
    1752           5 : {
    1753           5 :   return TALER_EXCHANGE_withdraw_with_age_proof_extra_blinding_seed (
    1754             :     curl_ctx,
    1755             :     keys,
    1756             :     exchange_url,
    1757             :     reserve_priv,
    1758             :     num_coins,
    1759             :     denoms_pub,
    1760             :     seed,
    1761             :     NULL,
    1762             :     max_age,
    1763             :     res_cb,
    1764             :     res_cb_cls);
    1765             : }
    1766             : 
    1767             : 
    1768             : void
    1769          75 : TALER_EXCHANGE_withdraw_cancel (
    1770             :   struct TALER_EXCHANGE_WithdrawHandle *wh)
    1771             : {
    1772          75 :   uint8_t kappa = wh->with_age_proof ? TALER_CNC_KAPPA : 1;
    1773             : 
    1774             :   /* Cleanup coin data */
    1775         156 :   for (unsigned int i = 0; i<wh->num_coins; i++)
    1776             :   {
    1777          81 :     struct CoinData *cd = &wh->coin_data[i];
    1778             : 
    1779         180 :     for (uint8_t k = 0; k < kappa; k++)
    1780             :     {
    1781          99 :       struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k];
    1782          99 :       struct CoinCandidate *can = &cd->candidates[k];
    1783             : 
    1784          99 :       TALER_blinded_planchet_free (&planchet->blinded_planchet);
    1785          99 :       TALER_denom_ewv_free (&can->details.blinding_values);
    1786          99 :       TALER_age_commitment_proof_free (&can->details.age_commitment_proof);
    1787             :     }
    1788          81 :     TALER_denom_pub_free (&cd->denom_pub.key);
    1789             :   }
    1790             : 
    1791          75 :   TALER_EXCHANGE_blinding_prepare_cancel (wh->blinding_prepare_handle);
    1792          75 :   TALER_EXCHANGE_withdraw_blinded_cancel (wh->withdraw_blinded_handle);
    1793          75 :   wh->blinding_prepare_handle = NULL;
    1794          75 :   wh->withdraw_blinded_handle = NULL;
    1795             : 
    1796          75 :   GNUNET_free (wh->coin_data);
    1797          75 :   TALER_EXCHANGE_keys_decref (wh->keys);
    1798          75 :   GNUNET_free (wh);
    1799          75 : }
    1800             : 
    1801             : 
    1802             : /**
    1803             :  * @brief Prepare the handler for blinded withdraw
    1804             :  *
    1805             :  * Allocates the handler struct and prepares all fields of the handler
    1806             :  * except the blinded planchets,
    1807             :  * which depend on them being age-restricted or not.
    1808             :  *
    1809             :  * @param curl_ctx the context for curl
    1810             :  * @param keys the exchange keys
    1811             :  * @param exchange_url the url to the exchange
    1812             :  * @param reserve_priv the reserve's private key
    1813             :  * @param res_cb the callback on result
    1814             :  * @param res_cb_cls the closure to pass on to the callback
    1815             :  * @return the handler
    1816             :  */
    1817             : static struct TALER_EXCHANGE_WithdrawBlindedHandle *
    1818          75 : setup_handler_common (
    1819             :   struct GNUNET_CURL_Context *curl_ctx,
    1820             :   struct TALER_EXCHANGE_Keys *keys,
    1821             :   const char *exchange_url,
    1822             :   const struct TALER_ReservePrivateKeyP *reserve_priv,
    1823             :   TALER_EXCHANGE_WithdrawBlindedCallback res_cb,
    1824             :   void *res_cb_cls)
    1825             : {
    1826             : 
    1827             :   struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh =
    1828          75 :     GNUNET_new (struct TALER_EXCHANGE_WithdrawBlindedHandle);
    1829             : 
    1830          75 :   wbh->keys = TALER_EXCHANGE_keys_incref (keys);
    1831          75 :   wbh->curl_ctx = curl_ctx;
    1832          75 :   wbh->reserve_priv = reserve_priv;
    1833          75 :   wbh->callback = res_cb;
    1834          75 :   wbh->callback_cls = res_cb_cls;
    1835          75 :   wbh->request_url = TALER_url_join (exchange_url,
    1836             :                                      "withdraw",
    1837             :                                      NULL);
    1838          75 :   GNUNET_CRYPTO_eddsa_key_get_public (
    1839          75 :     &wbh->reserve_priv->eddsa_priv,
    1840             :     &wbh->reserve_pub.eddsa_pub);
    1841             : 
    1842          75 :   return wbh;
    1843             : }
    1844             : 
    1845             : 
    1846             : struct TALER_EXCHANGE_WithdrawBlindedHandle *
    1847          70 : TALER_EXCHANGE_withdraw_blinded (
    1848             :   struct GNUNET_CURL_Context *curl_ctx,
    1849             :   struct TALER_EXCHANGE_Keys *keys,
    1850             :   const char *exchange_url,
    1851             :   const struct TALER_ReservePrivateKeyP *reserve_priv,
    1852             :   const struct TALER_BlindingMasterSeedP *blinding_seed,
    1853             :   size_t num_input,
    1854             :   const struct TALER_EXCHANGE_WithdrawBlindedCoinInput
    1855             :   blinded_input[static num_input],
    1856             :   TALER_EXCHANGE_WithdrawBlindedCallback res_cb,
    1857             :   void *res_cb_cls)
    1858          70 : {
    1859             :   struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh =
    1860          70 :     setup_handler_common (curl_ctx,
    1861             :                           keys,
    1862             :                           exchange_url,
    1863             :                           reserve_priv,
    1864             :                           res_cb,
    1865             :                           res_cb_cls);
    1866             : 
    1867          70 :   wbh->with_age_proof = false;
    1868          70 :   wbh->num_input = num_input;
    1869          70 :   wbh->blinded.input = blinded_input;
    1870          70 :   wbh->blinding_seed = blinding_seed;
    1871             : 
    1872          70 :   perform_withdraw_protocol (wbh);
    1873          70 :   return wbh;
    1874             : }
    1875             : 
    1876             : 
    1877             : struct TALER_EXCHANGE_WithdrawBlindedHandle *
    1878           5 : TALER_EXCHANGE_withdraw_blinded_with_age_proof (
    1879             :   struct GNUNET_CURL_Context *curl_ctx,
    1880             :   struct TALER_EXCHANGE_Keys *keys,
    1881             :   const char *exchange_url,
    1882             :   const struct TALER_ReservePrivateKeyP *reserve_priv,
    1883             :   const struct TALER_BlindingMasterSeedP *blinding_seed,
    1884             :   uint8_t max_age,
    1885             :   unsigned int num_input,
    1886             :   const struct TALER_EXCHANGE_WithdrawBlindedAgeRestrictedCoinInput
    1887             :   blinded_input[static num_input],
    1888             :   TALER_EXCHANGE_WithdrawBlindedCallback res_cb,
    1889             :   void *res_cb_cls)
    1890           5 : {
    1891             :   struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh =
    1892           5 :     setup_handler_common (curl_ctx,
    1893             :                           keys,
    1894             :                           exchange_url,
    1895             :                           reserve_priv,
    1896             :                           res_cb,
    1897             :                           res_cb_cls);
    1898             : 
    1899           5 :   wbh->with_age_proof = true;
    1900           5 :   wbh->max_age = max_age;
    1901           5 :   wbh->num_input = num_input;
    1902           5 :   wbh->blinded.with_age_proof_input = blinded_input;
    1903           5 :   wbh->blinding_seed = blinding_seed;
    1904             : 
    1905           5 :   perform_withdraw_protocol (wbh);
    1906           5 :   return wbh;
    1907             : }
    1908             : 
    1909             : 
    1910             : void
    1911         150 : TALER_EXCHANGE_withdraw_blinded_cancel (
    1912             :   struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh)
    1913             : {
    1914         150 :   if (NULL == wbh)
    1915          75 :     return;
    1916          75 :   if (NULL != wbh->job)
    1917             :   {
    1918           0 :     GNUNET_CURL_job_cancel (wbh->job);
    1919           0 :     wbh->job = NULL;
    1920             :   }
    1921          75 :   GNUNET_free (wbh->request_url);
    1922          75 :   TALER_EXCHANGE_keys_decref (wbh->keys);
    1923          75 :   TALER_curl_easy_post_finished (&wbh->post_ctx);
    1924          75 :   GNUNET_free (wbh);
    1925             : }
    1926             : 
    1927             : 
    1928             : /* exchange_api_withdraw.c */

Generated by: LCOV version 1.16