LCOV - code coverage report
Current view: top level - lib - exchange_api_withdraw.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 82.9 % 589 488
Test Date: 2026-01-28 06:10:56 Functions: 100.0 % 19 19

            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 "taler/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/taler_curl_lib.h"
      36              : #include "taler/taler_error_codes.h"
      37              : #include "taler/taler_json_lib.h"
      38              : #include "taler/taler_exchange_service.h"
      39              : #include "exchange_api_common.h"
      40              : #include "exchange_api_handle.h"
      41              : #include "taler/taler_signatures.h"
      42              : #include "exchange_api_curl_defaults.h"
      43              : #include "taler/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 2.0-1