exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

exchange_api_withdraw.c (55214B)


      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 withdraw_blinded_ok (
    416   struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh,
    417   const json_t *j_response)
    418 {
    419   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     GNUNET_JSON_spec_array_const ("ev_sigs",
    426                                   &j_sigs),
    427     GNUNET_JSON_spec_end ()
    428   };
    429 
    430   if (GNUNET_OK !=
    431       GNUNET_JSON_parse (j_response,
    432                          spec,
    433                          NULL, NULL))
    434   {
    435     GNUNET_break_op (0);
    436     return GNUNET_SYSERR;
    437   }
    438 
    439   if (wbh->num_input != json_array_size (j_sigs))
    440   {
    441     /* Number of coins generated does not match our expectation */
    442     GNUNET_break_op (0);
    443     return GNUNET_SYSERR;
    444   }
    445 
    446   {
    447     struct TALER_BlindedDenominationSignature denoms_sig[wbh->num_input];
    448 
    449     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       json_array_foreach (j_sigs, i, j_sig)
    459       {
    460         struct GNUNET_JSON_Specification ispec[] = {
    461           TALER_JSON_spec_blinded_denom_sig (NULL,
    462                                              &denoms_sig[i]),
    463           GNUNET_JSON_spec_end ()
    464         };
    465 
    466         if (GNUNET_OK !=
    467             GNUNET_JSON_parse (j_sig,
    468                                ispec,
    469                                NULL, NULL))
    470         {
    471           GNUNET_break_op (0);
    472           return GNUNET_SYSERR;
    473         }
    474       }
    475     }
    476 
    477     response.details.ok.num_sigs = wbh->num_input;
    478     response.details.ok.blinded_denom_sigs = denoms_sig;
    479     response.details.ok.planchets_h = wbh->planchets_h;
    480     wbh->callback (
    481       wbh->callback_cls,
    482       &response);
    483     /* Make sure the callback isn't called again */
    484     wbh->callback = NULL;
    485     /* Free resources */
    486     for (size_t i = 0; i < wbh->num_input; i++)
    487       TALER_blinded_denom_sig_free (&denoms_sig[i]);
    488   }
    489 
    490   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 withdraw_blinded_created (
    504   struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh,
    505   const json_t *j_response)
    506 {
    507   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     .details.created.num_coins = wbh->num_input,
    512   };
    513   struct TALER_ExchangeSignatureP exchange_sig;
    514   struct GNUNET_JSON_Specification spec[] = {
    515     GNUNET_JSON_spec_uint8 ("noreveal_index",
    516                             &response.details.created.noreveal_index),
    517     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
    518                                  &exchange_sig),
    519     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
    520                                  &response.details.created.exchange_pub),
    521     GNUNET_JSON_spec_end ()
    522   };
    523 
    524   if (GNUNET_OK!=
    525       GNUNET_JSON_parse (j_response,
    526                          spec,
    527                          NULL, NULL))
    528   {
    529     GNUNET_break_op (0);
    530     return GNUNET_SYSERR;
    531   }
    532 
    533   if (GNUNET_OK !=
    534       TALER_exchange_online_withdraw_age_confirmation_verify (
    535         &wbh->planchets_h,
    536         response.details.created.noreveal_index,
    537         &response.details.created.exchange_pub,
    538         &exchange_sig))
    539   {
    540     GNUNET_break_op (0);
    541     return GNUNET_SYSERR;
    542 
    543   }
    544 
    545   wbh->callback (wbh->callback_cls,
    546                  &response);
    547   /* make sure the callback isn't called again */
    548   wbh->callback = NULL;
    549 
    550   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 handle_withdraw_blinded_finished (
    564   void *cls,
    565   long response_code,
    566   const void *response)
    567 {
    568   struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh = cls;
    569   const json_t *j_response = response;
    570   struct TALER_EXCHANGE_WithdrawBlindedResponse wbr = {
    571     .hr.reply = j_response,
    572     .hr.http_status = (unsigned int) response_code
    573   };
    574 
    575   wbh->job = NULL;
    576   switch (response_code)
    577   {
    578   case 0:
    579     wbr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    580     break;
    581   case MHD_HTTP_OK:
    582     {
    583       if (GNUNET_OK !=
    584           withdraw_blinded_ok (
    585             wbh,
    586             j_response))
    587       {
    588         GNUNET_break_op (0);
    589         wbr.hr.http_status = 0;
    590         wbr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    591         break;
    592       }
    593       GNUNET_assert (NULL == wbh->callback);
    594       TALER_EXCHANGE_withdraw_blinded_cancel (wbh);
    595       return;
    596     }
    597   case MHD_HTTP_CREATED:
    598     if (GNUNET_OK !=
    599         withdraw_blinded_created (
    600           wbh,
    601           j_response))
    602     {
    603       GNUNET_break_op (0);
    604       wbr.hr.http_status = 0;
    605       wbr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    606       break;
    607     }
    608     GNUNET_assert (NULL == wbh->callback);
    609     TALER_EXCHANGE_withdraw_blinded_cancel (wbh);
    610     return;
    611   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     wbr.hr.ec = TALER_JSON_get_error_code (j_response);
    615     wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
    616     break;
    617   case MHD_HTTP_FORBIDDEN:
    618     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     wbr.hr.ec = TALER_JSON_get_error_code (j_response);
    623     wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
    624     break;
    625   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     wbr.hr.ec = TALER_JSON_get_error_code (j_response);
    631     wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
    632     break;
    633   case MHD_HTTP_CONFLICT:
    634     /* The age requirements might not have been met */
    635     wbr.hr.ec = TALER_JSON_get_error_code (j_response);
    636     wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
    637     break;
    638   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     wbr.hr.ec = TALER_JSON_get_error_code (j_response);
    644     wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
    645     break;
    646   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
    647     /* only validate reply is well-formed */
    648     {
    649       struct GNUNET_JSON_Specification spec[] = {
    650         GNUNET_JSON_spec_fixed_auto (
    651           "h_payto",
    652           &wbr.details.unavailable_for_legal_reasons.h_payto),
    653         GNUNET_JSON_spec_uint64 (
    654           "requirement_row",
    655           &wbr.details.unavailable_for_legal_reasons.requirement_row),
    656         GNUNET_JSON_spec_end ()
    657       };
    658 
    659       if (GNUNET_OK !=
    660           GNUNET_JSON_parse (j_response,
    661                              spec,
    662                              NULL, NULL))
    663       {
    664         GNUNET_break_op (0);
    665         wbr.hr.http_status = 0;
    666         wbr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    667         break;
    668       }
    669       break;
    670     }
    671   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     wbr.hr.ec = TALER_JSON_get_error_code (j_response);
    675     wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
    676     break;
    677   default:
    678     /* unexpected response code */
    679     GNUNET_break_op (0);
    680     wbr.hr.ec = TALER_JSON_get_error_code (j_response);
    681     wbr.hr.hint = TALER_JSON_get_error_hint (j_response);
    682     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     break;
    687   }
    688   wbh->callback (wbh->callback_cls,
    689                  &wbr);
    690   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 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   json_t *j_denoms = NULL;
    713   json_t *j_planchets = NULL;
    714   json_t *j_request_body = NULL;
    715   CURL *curlh = NULL;
    716   struct GNUNET_HashContext *coins_hctx = NULL;
    717   struct TALER_BlindedCoinHashP bch;
    718 
    719   GNUNET_assert (0 < wbh->num_input);
    720 
    721   FAIL_IF (GNUNET_OK !=
    722            TALER_amount_set_zero (wbh->keys->currency,
    723                                   &wbh->amount));
    724   FAIL_IF (GNUNET_OK !=
    725            TALER_amount_set_zero (wbh->keys->currency,
    726                                   &wbh->fee));
    727 
    728   /* Accumulate total value with fees */
    729   for (size_t i = 0; i < wbh->num_input; i++)
    730   {
    731     const struct TALER_EXCHANGE_DenomPublicKey *dpub =
    732       wbh->with_age_proof ?
    733       wbh->blinded.with_age_proof_input[i].denom_pub :
    734       wbh->blinded.input[i].denom_pub;
    735 
    736     FAIL_IF (0 >
    737              TALER_amount_add (&wbh->amount,
    738                                &wbh->amount,
    739                                &dpub->value));
    740     FAIL_IF (0 >
    741              TALER_amount_add (&wbh->fee,
    742                                &wbh->fee,
    743                                &dpub->fees.withdraw));
    744 
    745     if (GNUNET_CRYPTO_BSA_CS ==
    746         dpub->key.bsign_pub_key->cipher)
    747       GNUNET_assert (NULL != wbh->blinding_seed);
    748 
    749   }
    750 
    751   if (wbh->with_age_proof || wbh->max_age > 0)
    752   {
    753     wbh->age_mask =
    754       wbh->blinded.with_age_proof_input[0].denom_pub->key.age_mask;
    755 
    756     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     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    764                 "Attempting to withdraw from reserve %s\n",
    765                 TALER_B2S (&wbh->reserve_pub));
    766   }
    767 
    768   coins_hctx = GNUNET_CRYPTO_hash_context_start ();
    769   FAIL_IF (NULL == coins_hctx);
    770 
    771   j_denoms = json_array ();
    772   j_planchets = json_array ();
    773   FAIL_IF ((NULL == j_denoms) ||
    774            (NULL == j_planchets));
    775 
    776   for (size_t i  = 0; i< wbh->num_input; i++)
    777   {
    778     /* Build the denomination array */
    779     const struct TALER_EXCHANGE_DenomPublicKey *denom_pub =
    780       wbh->with_age_proof ?
    781       wbh->blinded.with_age_proof_input[i].denom_pub :
    782       wbh->blinded.input[i].denom_pub;
    783     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     FAIL_IF (wbh->with_age_proof &&
    788              (wbh->age_mask.bits != denom_pub->key.age_mask.bits));
    789 
    790     jdenom = GNUNET_JSON_from_data_auto (denom_h);
    791     FAIL_IF (NULL == jdenom);
    792     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   if (! wbh->with_age_proof)
    799   {
    800     for (size_t i  = 0; i< wbh->num_input; i++)
    801     {
    802       const struct TALER_PlanchetDetail *planchet =
    803         &wbh->blinded.input[i].planchet_details;
    804       json_t *jc = GNUNET_JSON_PACK (
    805         TALER_JSON_pack_blinded_planchet (
    806           NULL,
    807           &planchet->blinded_planchet));
    808       FAIL_IF (NULL == jc);
    809       FAIL_IF (0 > json_array_append_new (j_planchets,
    810                                           jc));
    811 
    812       TALER_coin_ev_hash (&planchet->blinded_planchet,
    813                           &planchet->denom_pub_hash,
    814                           &bch);
    815 
    816       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     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       batch_ctx = GNUNET_CRYPTO_hash_context_start ();
    834       FAIL_IF (NULL == batch_ctx);
    835 
    836       for (size_t i  = 0; i< wbh->num_input; i++)
    837       {
    838         const struct TALER_PlanchetDetail *planchet =
    839           &wbh->blinded.with_age_proof_input[i].planchet_details[k];
    840         json_t *jc = GNUNET_JSON_PACK (
    841           TALER_JSON_pack_blinded_planchet (
    842             NULL,
    843             &planchet->blinded_planchet));
    844 
    845         FAIL_IF (NULL == jc);
    846         FAIL_IF (0 > json_array_append_new (
    847                    j_planchets,
    848                    jc));
    849 
    850         TALER_coin_ev_hash (
    851           &planchet->blinded_planchet,
    852           &planchet->denom_pub_hash,
    853           &bch);
    854 
    855         GNUNET_CRYPTO_hash_context_read (
    856           batch_ctx,
    857           &bch,
    858           sizeof(bch));
    859       }
    860 
    861       GNUNET_CRYPTO_hash_context_finish (
    862         batch_ctx,
    863         &batch_h.hash);
    864       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   GNUNET_CRYPTO_hash_context_finish (
    873     coins_hctx,
    874     &wbh->planchets_h.hash);
    875   coins_hctx = NULL;
    876 
    877   /* Sign the request */
    878   TALER_wallet_withdraw_sign (
    879     &wbh->amount,
    880     &wbh->fee,
    881     &wbh->planchets_h,
    882     wbh->blinding_seed,
    883     wbh->with_age_proof ? &wbh->age_mask: NULL,
    884     wbh->with_age_proof ? wbh->max_age : 0,
    885     wbh->reserve_priv,
    886     &wbh->reserve_sig);
    887 
    888   /* Initiate the POST-request */
    889   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   FAIL_IF (NULL == j_request_body);
    907 
    908   if (NULL != wbh->blinding_seed)
    909   {
    910     json_t *j_seed = GNUNET_JSON_PACK (
    911       GNUNET_JSON_pack_data_auto ("blinding_seed",
    912                                   wbh->blinding_seed));
    913     GNUNET_assert (NULL != j_seed);
    914     GNUNET_assert (0 ==
    915                    json_object_update_new (
    916                      j_request_body,
    917                      j_seed));
    918   }
    919 
    920   curlh = TALER_EXCHANGE_curl_easy_get_ (wbh->request_url);
    921   FAIL_IF (NULL == curlh);
    922   FAIL_IF (GNUNET_OK !=
    923            TALER_curl_easy_post (
    924              &wbh->post_ctx,
    925              curlh,
    926              j_request_body));
    927   json_decref (j_request_body);
    928   j_request_body = NULL;
    929 
    930   wbh->job = GNUNET_CURL_job_add2 (
    931     wbh->curl_ctx,
    932     curlh,
    933     wbh->post_ctx.headers,
    934     &handle_withdraw_blinded_finished,
    935     wbh);
    936   FAIL_IF (NULL == wbh->job);
    937 
    938   /* No errors, return */
    939   return;
    940 
    941 ERROR:
    942   if (NULL != coins_hctx)
    943     GNUNET_CRYPTO_hash_context_abort (coins_hctx);
    944   if (NULL != j_denoms)
    945     json_decref (j_denoms);
    946   if (NULL != j_planchets)
    947     json_decref (j_planchets);
    948   if (NULL != j_request_body)
    949     json_decref (j_request_body);
    950   if (NULL != curlh)
    951     curl_easy_cleanup (curlh);
    952   TALER_EXCHANGE_withdraw_blinded_cancel (wbh);
    953   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 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   struct TALER_EXCHANGE_WithdrawHandle *wh = cls;
    972   struct TALER_EXCHANGE_WithdrawResponse resp = {
    973     .hr = wbr->hr,
    974   };
    975 
    976   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   switch (wbr->hr.http_status)
    984   {
    985   case MHD_HTTP_OK:
    986     {
    987       struct TALER_EXCHANGE_WithdrawCoinPrivateDetails
    988         details[GNUNET_NZL (wh->num_coins)];
    989       bool ok = true;
    990 
    991       GNUNET_assert (wh->num_coins == wbr->details.ok.num_sigs);
    992       memset (details,
    993               0,
    994               sizeof(details));
    995       resp.details.ok.num_sigs = wbr->details.ok.num_sigs;
    996       resp.details.ok.coin_details = details;
    997       resp.details.ok.planchets_h = wbr->details.ok.planchets_h;
    998       for (size_t n = 0; n<wh->num_coins; n++)
    999       {
   1000         const struct TALER_BlindedDenominationSignature *bsig =
   1001           &wbr->details.ok.blinded_denom_sigs[n];
   1002         struct CoinData *cd = &wh->coin_data[n];
   1003         struct TALER_EXCHANGE_WithdrawCoinPrivateDetails *coin = &details[n];
   1004         struct TALER_FreshCoin fresh_coin;
   1005 
   1006         *coin = wh->coin_data[n].candidates[0].details;
   1007         coin->planchet = wh->coin_data[n].planchet_details[0];
   1008         GNUNET_CRYPTO_eddsa_key_get_public (
   1009           &coin->coin_priv.eddsa_priv,
   1010           &coin->coin_pub.eddsa_pub);
   1011 
   1012         if (GNUNET_OK !=
   1013             TALER_planchet_to_coin (&cd->denom_pub.key,
   1014                                     bsig,
   1015                                     &coin->blinding_key,
   1016                                     &coin->coin_priv,
   1017                                     &coin->h_age_commitment,
   1018                                     &coin->h_coin_pub,
   1019                                     &coin->blinding_values,
   1020                                     &fresh_coin))
   1021         {
   1022           resp.hr.http_status = 0;
   1023           resp.hr.ec = TALER_EC_EXCHANGE_WITHDRAW_UNBLIND_FAILURE;
   1024           GNUNET_break_op (0);
   1025           ok = false;
   1026           break;
   1027         }
   1028         coin->denom_sig = fresh_coin.sig;
   1029       }
   1030       if (ok)
   1031       {
   1032         wh->callback (
   1033           wh->callback_cls,
   1034           &resp);
   1035         wh->callback = NULL;
   1036       }
   1037       for (size_t n = 0; n<wh->num_coins; n++)
   1038       {
   1039         struct TALER_EXCHANGE_WithdrawCoinPrivateDetails *coin = &details[n];
   1040 
   1041         TALER_denom_sig_free (&coin->denom_sig);
   1042       }
   1043       break;
   1044     }
   1045   case MHD_HTTP_CREATED:
   1046     resp.details.created  = wbr->details.created;
   1047     break;
   1048 
   1049   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
   1050     resp.details.unavailable_for_legal_reasons =
   1051       wbr->details.unavailable_for_legal_reasons;
   1052     break;
   1053 
   1054   default:
   1055     /* nothing to do here, .hr.ec and .hr.hint are all set already from previous response */
   1056     break;
   1057   }
   1058   if (NULL != wh->callback)
   1059   {
   1060     wh->callback (
   1061       wh->callback_cls,
   1062       &resp);
   1063     wh->callback = NULL;
   1064   }
   1065   TALER_EXCHANGE_withdraw_cancel (wh);
   1066 }
   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 copy_results_with_age_proof (
   1078   void *cls,
   1079   const struct TALER_EXCHANGE_WithdrawBlindedResponse *wbr)
   1080 {
   1081   /* The original handle from the top-level call to withdraw */
   1082   struct TALER_EXCHANGE_WithdrawHandle *wh = cls;
   1083   uint8_t k =  wbr->details.created.noreveal_index;
   1084   struct TALER_EXCHANGE_WithdrawCoinPrivateDetails details[wh->num_coins];
   1085   struct TALER_EXCHANGE_WithdrawResponse resp = {
   1086     .hr = wbr->hr,
   1087   };
   1088 
   1089   wh->withdraw_blinded_handle = NULL;
   1090 
   1091   switch (wbr->hr.http_status)
   1092   {
   1093   case MHD_HTTP_OK:
   1094     /* in the age-restricted case, this should not happen */
   1095     GNUNET_break_op (0);
   1096     break;
   1097 
   1098   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
   1099     resp.details.unavailable_for_legal_reasons =
   1100       wbr->details.unavailable_for_legal_reasons;
   1101     break;
   1102 
   1103   case MHD_HTTP_CREATED:
   1104     {
   1105       GNUNET_assert (wh->num_coins == wbr->details.created.num_coins);
   1106       resp.details.created = wbr->details.created;
   1107       resp.details.created.coin_details = details;
   1108       resp.details.created.kappa_seed = wh->kappa_seed;
   1109       memset (details,
   1110               0,
   1111               sizeof(details));
   1112       for (size_t n = 0; n< wh->num_coins; n++)
   1113       {
   1114         details[n] = wh->coin_data[n].candidates[k].details;
   1115         details[n].planchet = wh->coin_data[n].planchet_details[k];
   1116       }
   1117       break;
   1118     }
   1119 
   1120   default:
   1121     break;
   1122   }
   1123 
   1124   wh->callback (
   1125     wh->callback_cls,
   1126     &resp);
   1127   wh->callback = NULL;
   1128   TALER_EXCHANGE_withdraw_cancel (wh);
   1129 }
   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 call_withdraw_blinded (
   1139   struct TALER_EXCHANGE_WithdrawHandle *wh)
   1140 {
   1141 
   1142   GNUNET_assert (NULL == wh->blinding_prepare_handle);
   1143 
   1144   if (! wh->with_age_proof)
   1145   {
   1146     struct TALER_EXCHANGE_WithdrawBlindedCoinInput input[wh->num_coins];
   1147 
   1148     memset (input,
   1149             0,
   1150             sizeof(input));
   1151 
   1152     /* Prepare the blinded planchets as input */
   1153     for (size_t n = 0; n < wh->num_coins; n++)
   1154     {
   1155       input[n].denom_pub =
   1156         &wh->coin_data[n].denom_pub;
   1157       input[n].planchet_details =
   1158         *wh->coin_data[n].planchet_details;
   1159     }
   1160 
   1161     wh->withdraw_blinded_handle =
   1162       TALER_EXCHANGE_withdraw_blinded (
   1163         wh->curl_ctx,
   1164         wh->keys,
   1165         wh->exchange_url,
   1166         wh->reserve_priv,
   1167         wh->has_blinding_seed ? &wh->blinding_seed : NULL,
   1168         wh->num_coins,
   1169         input,
   1170         &copy_results,
   1171         wh);
   1172   }
   1173   else
   1174   {  /* age restricted case */
   1175     struct TALER_EXCHANGE_WithdrawBlindedAgeRestrictedCoinInput
   1176       ari[wh->num_coins];
   1177 
   1178     memset (ari,
   1179             0,
   1180             sizeof(ari));
   1181 
   1182     /* Prepare the blinded planchets as input */
   1183     for (size_t n = 0; n < wh->num_coins; n++)
   1184     {
   1185       ari[n].denom_pub = &wh->coin_data[n].denom_pub;
   1186       for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
   1187         ari[n].planchet_details[k] =
   1188           wh->coin_data[n].planchet_details[k];
   1189     }
   1190 
   1191     wh->withdraw_blinded_handle =
   1192       TALER_EXCHANGE_withdraw_blinded_with_age_proof (
   1193         wh->curl_ctx,
   1194         wh->keys,
   1195         wh->exchange_url,
   1196         wh->reserve_priv,
   1197         wh->has_blinding_seed ? &wh->blinding_seed : NULL,
   1198         wh->max_age,
   1199         wh->num_coins,
   1200         ari,
   1201         &copy_results_with_age_proof,
   1202         wh);
   1203   }
   1204 }
   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 blinding_prepare_done (
   1215   void *cls,
   1216   const struct TALER_EXCHANGE_BlindingPrepareResponse *bpr)
   1217 {
   1218   struct BlindingPrepareClosure *bpcls = cls;
   1219   struct TALER_EXCHANGE_WithdrawHandle *wh = bpcls->withdraw_handle;
   1220 
   1221   wh->blinding_prepare_handle = NULL;
   1222   switch (bpr->hr.http_status)
   1223   {
   1224   case MHD_HTTP_OK:
   1225     {
   1226       bool success = false;
   1227       size_t num = bpr->details.ok.num_blinding_values;
   1228 
   1229       GNUNET_assert (0 != num);
   1230       GNUNET_assert (num == bpcls->num_nonces);
   1231       for (size_t i = 0; i < bpcls->num_prepare_coins; i++)
   1232       {
   1233         struct TALER_PlanchetDetail *planchet = bpcls->coins[i].planchet;
   1234         struct CoinCandidate *can = bpcls->coins[i].candidate;
   1235         size_t cs_idx = bpcls->coins[i].cs_idx;
   1236 
   1237         GNUNET_assert (NULL != can);
   1238         GNUNET_assert (NULL != planchet);
   1239         success = false;
   1240 
   1241         /* Complete the initialization of the coin with CS denomination */
   1242         TALER_denom_ewv_copy (
   1243           &can->details.blinding_values,
   1244           &bpr->details.ok.blinding_values[cs_idx]);
   1245 
   1246         GNUNET_assert (GNUNET_CRYPTO_BSA_CS ==
   1247                        can->details.blinding_values.blinding_inputs->cipher);
   1248 
   1249         TALER_planchet_setup_coin_priv (
   1250           &can->details.secret,
   1251           &can->details.blinding_values,
   1252           &can->details.coin_priv);
   1253 
   1254         TALER_planchet_blinding_secret_create (
   1255           &can->details.secret,
   1256           &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         if (GNUNET_OK !=
   1262             TALER_planchet_prepare (
   1263               bpcls->coins[i].denom_pub,
   1264               &can->details.blinding_values,
   1265               &can->details.blinding_key,
   1266               &bpcls->nonces[cs_idx],
   1267               &can->details.coin_priv,
   1268               &can->details.h_age_commitment,
   1269               &can->details.h_coin_pub,
   1270               planchet))
   1271         {
   1272           GNUNET_break (0);
   1273           break;
   1274         }
   1275 
   1276         TALER_coin_ev_hash (&planchet->blinded_planchet,
   1277                             &planchet->denom_pub_hash,
   1278                             &can->blinded_coin_h);
   1279         success = true;
   1280       }
   1281 
   1282       /* /blinding-prepare is done, we can now perform the
   1283        * actual withdraw operation */
   1284       if (success)
   1285         call_withdraw_blinded (wh);
   1286       goto cleanup;
   1287     }
   1288   default:
   1289     {
   1290       /* We got an error condition during blinding prepare that we need to report */
   1291       struct TALER_EXCHANGE_WithdrawResponse resp = {
   1292         .hr = bpr->hr
   1293       };
   1294 
   1295       wh->callback (
   1296         wh->callback_cls,
   1297         &resp);
   1298 
   1299       wh->callback = NULL;
   1300       break;
   1301     }
   1302   }
   1303   TALER_EXCHANGE_withdraw_cancel (wh);
   1304 cleanup:
   1305   GNUNET_free (bpcls->coins);
   1306   GNUNET_free (bpcls->nonces);
   1307   GNUNET_free (bpcls);
   1308 }
   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 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   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   GNUNET_assert (0 < num_coins);
   1350 
   1351   wh->num_coins = num_coins;
   1352   wh->max_age = max_age;
   1353   wh->age_mask = denoms_pub[0].key.age_mask;
   1354   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   for (size_t i =0; i< wh->num_coins; i++)
   1360   {
   1361     if (GNUNET_CRYPTO_BSA_CS ==
   1362         denoms_pub[i].key.bsign_pub_key->cipher)
   1363       cs_num++;
   1364   }
   1365 
   1366   if (wh->with_age_proof)
   1367   {
   1368     kappa = TALER_CNC_KAPPA;
   1369   }
   1370   else
   1371   {
   1372     kappa = 1;
   1373   }
   1374 
   1375   {
   1376     struct TALER_PlanchetMasterSecretP secrets[kappa][num_coins];
   1377     struct TALER_EXCHANGE_NonceKey cs_nonce_keys[GNUNET_NZL (cs_num)];
   1378     uint32_t cs_indices[GNUNET_NZL (cs_num)];
   1379 
   1380     size_t cs_denom_idx = 0;
   1381     size_t cs_coin_idx = 0;
   1382 
   1383     if (wh->with_age_proof)
   1384     {
   1385       TALER_withdraw_expand_kappa_seed (seed,
   1386                                         &wh->kappa_seed);
   1387       for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
   1388       {
   1389         TALER_withdraw_expand_secrets (
   1390           num_coins,
   1391           &wh->kappa_seed.tuple[k],
   1392           secrets[k]);
   1393       }
   1394     }
   1395     else
   1396     {
   1397       TALER_withdraw_expand_secrets (
   1398         num_coins,
   1399         seed,
   1400         secrets[0]);
   1401     }
   1402 
   1403     if (0 < cs_num)
   1404     {
   1405       memset (cs_nonce_keys,
   1406               0,
   1407               sizeof(cs_nonce_keys));
   1408       cs_closure = GNUNET_new (struct BlindingPrepareClosure);
   1409       cs_closure->withdraw_handle = wh;
   1410       cs_closure->num_prepare_coins = cs_num * kappa;
   1411       GNUNET_assert ((1 == kappa) || (cs_num * kappa > cs_num));
   1412       cs_closure->coins =
   1413         GNUNET_new_array (cs_closure->num_prepare_coins,
   1414                           struct BlindingPrepareCoinData);
   1415       cs_closure->num_nonces = cs_num;
   1416       cs_closure->nonces =
   1417         GNUNET_new_array (cs_closure->num_nonces,
   1418                           union GNUNET_CRYPTO_BlindSessionNonce);
   1419     }
   1420     else
   1421     {
   1422       cs_closure = NULL;
   1423     }
   1424 
   1425     for (uint32_t i = 0; i < wh->num_coins; i++)
   1426     {
   1427       struct CoinData *cd = &wh->coin_data[i];
   1428       bool age_denom = (0 != denoms_pub[i].key.age_mask.bits);
   1429 
   1430       cd->denom_pub = denoms_pub[i];
   1431       /* The age mask must be the same for all coins */
   1432       FAIL_IF (wh->with_age_proof &&
   1433                (0 ==  denoms_pub[i].key.age_mask.bits));
   1434       FAIL_IF (wh->age_mask.bits !=
   1435                denoms_pub[i].key.age_mask.bits);
   1436       TALER_denom_pub_copy (&cd->denom_pub.key,
   1437                             &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       if (GNUNET_CRYPTO_BSA_CS ==
   1443           cd->denom_pub.key.bsign_pub_key->cipher)
   1444       {
   1445         GNUNET_assert (cs_denom_idx<cs_num);
   1446         cs_indices[cs_denom_idx] = i;
   1447         cs_nonce_keys[cs_denom_idx].cnc_num = i;
   1448         cs_nonce_keys[cs_denom_idx].pk = &cd->denom_pub;
   1449         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       for (uint8_t k = 0; k < kappa; k++)
   1457       {
   1458         struct CoinCandidate *can = &cd->candidates[k];
   1459         struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k];
   1460 
   1461         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         if (age_denom)
   1468         {
   1469           /* Derive the age restriction from the given secret and
   1470            * the maximum age */
   1471           TALER_age_restriction_from_secret (
   1472             &can->details.secret,
   1473             &wh->age_mask,
   1474             wh->max_age,
   1475             &can->details.age_commitment_proof);
   1476 
   1477           TALER_age_commitment_hash (
   1478             &can->details.age_commitment_proof.commitment,
   1479             &can->details.h_age_commitment);
   1480         }
   1481 
   1482         switch (cd->denom_pub.key.bsign_pub_key->cipher)
   1483         {
   1484         case GNUNET_CRYPTO_BSA_RSA:
   1485           TALER_denom_ewv_copy (&can->details.blinding_values,
   1486                                 TALER_denom_ewv_rsa_singleton ());
   1487           TALER_planchet_setup_coin_priv (&can->details.secret,
   1488                                           &can->details.blinding_values,
   1489                                           &can->details.coin_priv);
   1490           TALER_planchet_blinding_secret_create (&can->details.secret,
   1491                                                  &can->details.blinding_values,
   1492                                                  &can->details.blinding_key);
   1493           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           TALER_coin_ev_hash (&planchet->blinded_planchet,
   1505                               &planchet->denom_pub_hash,
   1506                               &can->blinded_coin_h);
   1507 
   1508           break;
   1509 
   1510         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             cs_closure->coins[cs_coin_idx].candidate = can;
   1517             cs_closure->coins[cs_coin_idx].planchet = planchet;
   1518             cs_closure->coins[cs_coin_idx].denom_pub = &cd->denom_pub.key;
   1519             cs_closure->coins[cs_coin_idx].cs_idx = i;
   1520             cs_closure->coins[cs_coin_idx].age_denom = age_denom;
   1521             cs_coin_idx++;
   1522             break;
   1523           }
   1524         default:
   1525           FAIL_IF (1);
   1526         }
   1527       }
   1528     }
   1529 
   1530     if (0 < cs_num)
   1531     {
   1532       if (NULL != blinding_seed)
   1533       {
   1534         wh->blinding_seed = *blinding_seed;
   1535       }
   1536       else
   1537       {
   1538         TALER_cs_withdraw_seed_to_blinding_seed (
   1539           seed,
   1540           &wh->blinding_seed);
   1541       }
   1542       wh->has_blinding_seed = true;
   1543 
   1544       TALER_cs_derive_only_cs_blind_nonces_from_seed (
   1545         &wh->blinding_seed,
   1546         false, /* not for melt */
   1547         cs_num,
   1548         cs_indices,
   1549         cs_closure->nonces);
   1550 
   1551       wh->blinding_prepare_handle =
   1552         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       FAIL_IF (NULL == wh->blinding_prepare_handle);
   1561     }
   1562   }
   1563   return GNUNET_OK;
   1564 
   1565 ERROR:
   1566   if (0<cs_num)
   1567   {
   1568     GNUNET_free (cs_closure->nonces);
   1569     GNUNET_free (cs_closure);
   1570   }
   1571   TALER_EXCHANGE_withdraw_cancel (wh);
   1572   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 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   wh = GNUNET_new (struct TALER_EXCHANGE_WithdrawHandle);
   1601   wh->exchange_url = exchange_url;
   1602   wh->keys = TALER_EXCHANGE_keys_incref (keys);
   1603   wh->curl_ctx = curl_ctx;
   1604   wh->reserve_priv = reserve_priv;
   1605   wh->callback = res_cb;
   1606   wh->callback_cls = res_cb_cls;
   1607 
   1608   return wh;
   1609 }
   1610 
   1611 
   1612 struct TALER_EXCHANGE_WithdrawHandle *
   1613 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 {
   1626   struct TALER_EXCHANGE_WithdrawHandle *wh;
   1627 
   1628   wh = setup_withdraw_handle (curl_ctx,
   1629                               keys,
   1630                               exchange_url,
   1631                               reserve_priv,
   1632                               res_cb,
   1633                               res_cb_cls);
   1634   GNUNET_assert (NULL != wh);
   1635   wh->with_age_proof = false;
   1636 
   1637   if (GNUNET_OK !=
   1638       prepare_coins (wh,
   1639                      num_coins,
   1640                      opaque_max_age,
   1641                      denoms_pub,
   1642                      seed,
   1643                      blinding_seed))
   1644   {
   1645     GNUNET_free (wh);
   1646     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   if (NULL == wh->blinding_prepare_handle)
   1655     call_withdraw_blinded (wh);
   1656 
   1657   return wh;
   1658 }
   1659 
   1660 
   1661 struct TALER_EXCHANGE_WithdrawHandle *
   1662 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 {
   1674   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 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 {
   1704   struct TALER_EXCHANGE_WithdrawHandle *wh;
   1705 
   1706   wh = setup_withdraw_handle (curl_ctx,
   1707                               keys,
   1708                               exchange_url,
   1709                               reserve_priv,
   1710                               res_cb,
   1711                               res_cb_cls);
   1712   GNUNET_assert (NULL != wh);
   1713 
   1714   wh->with_age_proof = true;
   1715 
   1716   if (GNUNET_OK !=
   1717       prepare_coins (wh,
   1718                      num_coins,
   1719                      max_age,
   1720                      denoms_pub,
   1721                      seed,
   1722                      blinding_seed))
   1723   {
   1724     GNUNET_free (wh);
   1725     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   if (NULL == wh->blinding_prepare_handle)
   1734     call_withdraw_blinded (wh);
   1735 
   1736   return wh;
   1737 }
   1738 
   1739 
   1740 struct TALER_EXCHANGE_WithdrawHandle *
   1741 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 {
   1753   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 TALER_EXCHANGE_withdraw_cancel (
   1770   struct TALER_EXCHANGE_WithdrawHandle *wh)
   1771 {
   1772   uint8_t kappa = wh->with_age_proof ? TALER_CNC_KAPPA : 1;
   1773 
   1774   /* Cleanup coin data */
   1775   for (unsigned int i = 0; i<wh->num_coins; i++)
   1776   {
   1777     struct CoinData *cd = &wh->coin_data[i];
   1778 
   1779     for (uint8_t k = 0; k < kappa; k++)
   1780     {
   1781       struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k];
   1782       struct CoinCandidate *can = &cd->candidates[k];
   1783 
   1784       TALER_blinded_planchet_free (&planchet->blinded_planchet);
   1785       TALER_denom_ewv_free (&can->details.blinding_values);
   1786       TALER_age_commitment_proof_free (&can->details.age_commitment_proof);
   1787     }
   1788     TALER_denom_pub_free (&cd->denom_pub.key);
   1789   }
   1790 
   1791   TALER_EXCHANGE_blinding_prepare_cancel (wh->blinding_prepare_handle);
   1792   TALER_EXCHANGE_withdraw_blinded_cancel (wh->withdraw_blinded_handle);
   1793   wh->blinding_prepare_handle = NULL;
   1794   wh->withdraw_blinded_handle = NULL;
   1795 
   1796   GNUNET_free (wh->coin_data);
   1797   TALER_EXCHANGE_keys_decref (wh->keys);
   1798   GNUNET_free (wh);
   1799 }
   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 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     GNUNET_new (struct TALER_EXCHANGE_WithdrawBlindedHandle);
   1829 
   1830   wbh->keys = TALER_EXCHANGE_keys_incref (keys);
   1831   wbh->curl_ctx = curl_ctx;
   1832   wbh->reserve_priv = reserve_priv;
   1833   wbh->callback = res_cb;
   1834   wbh->callback_cls = res_cb_cls;
   1835   wbh->request_url = TALER_url_join (exchange_url,
   1836                                      "withdraw",
   1837                                      NULL);
   1838   GNUNET_CRYPTO_eddsa_key_get_public (
   1839     &wbh->reserve_priv->eddsa_priv,
   1840     &wbh->reserve_pub.eddsa_pub);
   1841 
   1842   return wbh;
   1843 }
   1844 
   1845 
   1846 struct TALER_EXCHANGE_WithdrawBlindedHandle *
   1847 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 {
   1859   struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh =
   1860     setup_handler_common (curl_ctx,
   1861                           keys,
   1862                           exchange_url,
   1863                           reserve_priv,
   1864                           res_cb,
   1865                           res_cb_cls);
   1866 
   1867   wbh->with_age_proof = false;
   1868   wbh->num_input = num_input;
   1869   wbh->blinded.input = blinded_input;
   1870   wbh->blinding_seed = blinding_seed;
   1871 
   1872   perform_withdraw_protocol (wbh);
   1873   return wbh;
   1874 }
   1875 
   1876 
   1877 struct TALER_EXCHANGE_WithdrawBlindedHandle *
   1878 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 {
   1891   struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh =
   1892     setup_handler_common (curl_ctx,
   1893                           keys,
   1894                           exchange_url,
   1895                           reserve_priv,
   1896                           res_cb,
   1897                           res_cb_cls);
   1898 
   1899   wbh->with_age_proof = true;
   1900   wbh->max_age = max_age;
   1901   wbh->num_input = num_input;
   1902   wbh->blinded.with_age_proof_input = blinded_input;
   1903   wbh->blinding_seed = blinding_seed;
   1904 
   1905   perform_withdraw_protocol (wbh);
   1906   return wbh;
   1907 }
   1908 
   1909 
   1910 void
   1911 TALER_EXCHANGE_withdraw_blinded_cancel (
   1912   struct TALER_EXCHANGE_WithdrawBlindedHandle *wbh)
   1913 {
   1914   if (NULL == wbh)
   1915     return;
   1916   if (NULL != wbh->job)
   1917   {
   1918     GNUNET_CURL_job_cancel (wbh->job);
   1919     wbh->job = NULL;
   1920   }
   1921   GNUNET_free (wbh->request_url);
   1922   TALER_EXCHANGE_keys_decref (wbh->keys);
   1923   TALER_curl_easy_post_finished (&wbh->post_ctx);
   1924   GNUNET_free (wbh);
   1925 }
   1926 
   1927 
   1928 /* exchange_api_withdraw.c */