exchange

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

exchange_api_post-withdraw.c (32709B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2023-2026 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_post-withdraw.c
     19  * @brief Implementation of /withdraw requests
     20  * @author Özgür Kesim
     21  */
     22 #include "taler/platform.h"
     23 #include <gnunet/gnunet_common.h>
     24 #include <jansson.h>
     25 #include <microhttpd.h> /* just for HTTP status codes */
     26 #include <gnunet/gnunet_util_lib.h>
     27 #include <gnunet/gnunet_json_lib.h>
     28 #include <gnunet/gnunet_curl_lib.h>
     29 #include <sys/wait.h>
     30 #include "taler/taler_curl_lib.h"
     31 #include "taler/taler_error_codes.h"
     32 #include "taler/taler_json_lib.h"
     33 #include "taler/taler_exchange_service.h"
     34 #include "exchange_api_common.h"
     35 #include "exchange_api_handle.h"
     36 #include "taler/taler_signatures.h"
     37 #include "exchange_api_curl_defaults.h"
     38 #include "taler/taler_util.h"
     39 
     40 /**
     41  * A CoinCandidate is populated from a master secret.
     42  * The data is copied from and generated out of the client's input.
     43  */
     44 struct CoinCandidate
     45 {
     46   /**
     47    * The details derived form the master secrets
     48    */
     49   struct TALER_EXCHANGE_WithdrawCoinPrivateDetails details;
     50 
     51   /**
     52    * Blinded hash of the coin
     53    **/
     54   struct TALER_BlindedCoinHashP blinded_coin_h;
     55 
     56 };
     57 
     58 
     59 /**
     60  * Data we keep per coin in the batch.
     61  * This is copied from and generated out of the input provided
     62  * by the client.
     63  */
     64 struct CoinData
     65 {
     66   /**
     67    * The denomination of the coin.
     68    */
     69   struct TALER_EXCHANGE_DenomPublicKey denom_pub;
     70 
     71   /**
     72    * The Candidates for the coin.  If the batch is not age-restricted,
     73    * only index 0 is used.
     74    */
     75   struct CoinCandidate candidates[TALER_CNC_KAPPA];
     76 
     77   /**
     78    * Details of the planchet(s).  If the batch is not age-restricted,
     79    * only index 0 is used.
     80    */
     81   struct TALER_PlanchetDetail planchet_details[TALER_CNC_KAPPA];
     82 };
     83 
     84 
     85 /**
     86  * Per-CS-coin data needed to complete the coin after /blinding-prepare.
     87  */
     88 struct BlindingPrepareCoinData
     89 {
     90   /**
     91    * Pointer to the candidate in CoinData.candidates,
     92    * to continue to build its contents based on the results from /blinding-prepare
     93    */
     94   struct CoinCandidate *candidate;
     95 
     96   /**
     97    * Planchet to finally generate in the corresponding candidate
     98    * in CoinData.planchet_details
     99    */
    100   struct TALER_PlanchetDetail *planchet;
    101 
    102   /**
    103    * Denomination information, needed for the
    104    * step after /blinding-prepare
    105    */
    106   const struct TALER_DenominationPublicKey *denom_pub;
    107 
    108   /**
    109    * True, if denomination supports age restriction
    110    */
    111   bool age_denom;
    112 
    113   /**
    114    * The index into the array of returned values from the call to
    115    * /blinding-prepare that are to be used for this coin.
    116    */
    117   size_t cs_idx;
    118 
    119 };
    120 
    121 
    122 /**
    123  * A /withdraw request-handle for calls from
    124  * a wallet, i. e. when blinding data is available.
    125  */
    126 struct TALER_EXCHANGE_PostWithdrawHandle
    127 {
    128 
    129   /**
    130    * The base-URL of the exchange.
    131    */
    132   const char *exchange_url;
    133 
    134   /**
    135    * Seed to derive of all seeds for the coins.
    136    */
    137   struct TALER_WithdrawMasterSeedP seed;
    138 
    139   /**
    140    * If @e with_age_proof is true, the derived TALER_CNC_KAPPA many
    141    * seeds for candidate batches.
    142    */
    143   struct TALER_KappaWithdrawMasterSeedP kappa_seed;
    144 
    145   /**
    146    * True if @e blinding_seed is filled, that is, if
    147    * any of the denominations is of cipher type CS
    148    */
    149   bool has_blinding_seed;
    150 
    151   /**
    152    * Seed used for the derivation of blinding factors for denominations
    153    * with Clause-Schnorr cipher.  We derive this from the master seed
    154    * for the withdraw, but independent from the other planchet seeds.
    155    * Only valid when @e has_blinding_seed is true;
    156    */
    157   struct TALER_BlindingMasterSeedP blinding_seed;
    158 
    159   /**
    160    * Reserve private key.
    161    */
    162   const struct TALER_ReservePrivateKeyP *reserve_priv;
    163 
    164   /**
    165    * Reserve public key, calculated
    166    */
    167   struct TALER_ReservePublicKeyP reserve_pub;
    168 
    169   /**
    170    * Signature of the reserve for the request, calculated after all
    171    * parameters for the coins are collected.
    172    */
    173   struct TALER_ReserveSignatureP reserve_sig;
    174 
    175   /*
    176    * The denomination keys of the exchange
    177    */
    178   struct TALER_EXCHANGE_Keys *keys;
    179 
    180   /**
    181    * True, if the withdraw is for age-restricted coins, with age-proof.
    182    * The denominations MUST support age restriction.
    183    */
    184   bool with_age_proof;
    185 
    186   /**
    187    * If @e with_age_proof is true, the age mask, extracted
    188    * from the denominations.
    189    * MUST be the same for all denominations.
    190    */
    191   struct TALER_AgeMask age_mask;
    192 
    193   /**
    194    * The maximum age to commit to.  If @e with_age_proof
    195    * is true, the client will need to proof the correct setting
    196    * of age-restriction on the coins via an additional call
    197    * to /reveal-withdraw.
    198    */
    199   uint8_t max_age;
    200 
    201   /**
    202    * Length of the @e coin_data Array
    203    */
    204   size_t num_coins;
    205 
    206   /**
    207    * Array of per-coin data
    208    */
    209   struct CoinData *coin_data;
    210 
    211   /**
    212    * Context for curl.
    213    */
    214   struct GNUNET_CURL_Context *curl_ctx;
    215 
    216   /**
    217    * Function to call with withdraw response results.
    218    */
    219   TALER_EXCHANGE_PostWithdrawCallback callback;
    220 
    221   /**
    222    * Closure for @e callback
    223    */
    224   void *callback_cls;
    225 
    226   /**
    227    * The handler for the call to /blinding-prepare, needed for CS denominations.
    228    * NULL until _start is called for CS denominations, or when no CS denoms.
    229    */
    230   struct TALER_EXCHANGE_PostBlindingPrepareHandle *blinding_prepare_handle;
    231 
    232   /**
    233    * The Handler for the actual call to the exchange
    234    */
    235   struct TALER_EXCHANGE_PostWithdrawBlindedHandle *withdraw_blinded_handle;
    236 
    237   /**
    238    * Number of CS denomination coin entries in @e bp_coins.
    239    * Zero if no CS denominations.
    240    */
    241   size_t num_bp_coins;
    242 
    243   /**
    244    * Array of @e num_bp_coins coin data for the blinding-prepare step.
    245    */
    246   struct BlindingPrepareCoinData *bp_coins;
    247 
    248   /**
    249    * Number of nonces in @e bp_nonces.
    250    */
    251   size_t num_bp_nonces;
    252 
    253   /**
    254    * Array of @e num_bp_nonces nonces for CS denominations.
    255    */
    256   union GNUNET_CRYPTO_BlindSessionNonce *bp_nonces;
    257 
    258   /**
    259    * Nonce keys for the blinding-prepare call.
    260    */
    261   struct TALER_EXCHANGE_NonceKey *bp_nonce_keys;
    262 
    263   /**
    264    * Number of nonce keys in @e bp_nonce_keys.
    265    */
    266   size_t num_bp_nonce_keys;
    267 
    268   /**
    269    * Array of @e init_num_coins denomination public keys.
    270    * NULL after _start is called.
    271    */
    272   struct TALER_EXCHANGE_DenomPublicKey *init_denoms_pub;
    273 
    274   /**
    275    * Number of coins provided in @e init_denoms_pub.
    276    */
    277   size_t init_num_coins;
    278 
    279   struct
    280   {
    281 
    282     /**
    283      * True if @e blinding_seed is filled, that is, if
    284      * any of the denominations is of cipher type CS
    285      */
    286     bool has_blinding_seed;
    287 
    288     /**
    289      * Seed used for the derivation of blinding factors for denominations
    290      * with Clause-Schnorr cipher.  We derive this from the master seed
    291      * for the withdraw, but independent from the other planchet seeds.
    292      * Only valid when @e has_blinding_seed is true;
    293      */
    294     struct TALER_BlindingMasterSeedP blinding_seed;
    295 
    296   } options;
    297 };
    298 
    299 
    300 /**
    301  * @brief Callback to copy the results from the call to post_withdraw_blinded
    302  * in the non-age-restricted case to the result for the originating call.
    303  *
    304  * @param cls struct TALER_EXCHANGE_PostWithdrawHandle
    305  * @param wbr The response
    306  */
    307 static void
    308 copy_results (
    309   void *cls,
    310   const struct TALER_EXCHANGE_PostWithdrawBlindedResponse *wbr)
    311 {
    312   /* The original handle from the top-level call to withdraw */
    313   struct TALER_EXCHANGE_PostWithdrawHandle *wh = cls;
    314   struct TALER_EXCHANGE_PostWithdrawResponse resp = {
    315     .hr = wbr->hr,
    316   };
    317 
    318   wh->withdraw_blinded_handle = NULL;
    319 
    320   /**
    321    * The withdraw protocol has been performed with blinded data.
    322    * Now the response can be copied as is, except for the MHD_HTTP_OK case,
    323    * in which we now need to perform the unblinding.
    324    */
    325   switch (wbr->hr.http_status)
    326   {
    327   case MHD_HTTP_OK:
    328     {
    329       struct TALER_EXCHANGE_WithdrawCoinPrivateDetails
    330         details[GNUNET_NZL (wh->num_coins)];
    331       bool ok = true;
    332 
    333       GNUNET_assert (wh->num_coins == wbr->details.ok.num_sigs);
    334       memset (details,
    335               0,
    336               sizeof(details));
    337       resp.details.ok.num_sigs = wbr->details.ok.num_sigs;
    338       resp.details.ok.coin_details = details;
    339       resp.details.ok.planchets_h = wbr->details.ok.planchets_h;
    340       for (size_t n = 0; n<wh->num_coins; n++)
    341       {
    342         const struct TALER_BlindedDenominationSignature *bsig =
    343           &wbr->details.ok.blinded_denom_sigs[n];
    344         struct CoinData *cd = &wh->coin_data[n];
    345         struct TALER_EXCHANGE_WithdrawCoinPrivateDetails *coin = &details[n];
    346         struct TALER_FreshCoin fresh_coin;
    347 
    348         *coin = wh->coin_data[n].candidates[0].details;
    349         coin->planchet = wh->coin_data[n].planchet_details[0];
    350         GNUNET_CRYPTO_eddsa_key_get_public (
    351           &coin->coin_priv.eddsa_priv,
    352           &coin->coin_pub.eddsa_pub);
    353 
    354         if (GNUNET_OK !=
    355             TALER_planchet_to_coin (&cd->denom_pub.key,
    356                                     bsig,
    357                                     &coin->blinding_key,
    358                                     &coin->coin_priv,
    359                                     &coin->h_age_commitment,
    360                                     &coin->h_coin_pub,
    361                                     &coin->blinding_values,
    362                                     &fresh_coin))
    363         {
    364           resp.hr.http_status = 0;
    365           resp.hr.ec = TALER_EC_EXCHANGE_WITHDRAW_UNBLIND_FAILURE;
    366           GNUNET_break_op (0);
    367           ok = false;
    368           break;
    369         }
    370         coin->denom_sig = fresh_coin.sig;
    371       }
    372       if (ok)
    373       {
    374         wh->callback (
    375           wh->callback_cls,
    376           &resp);
    377         wh->callback = NULL;
    378       }
    379       for (size_t n = 0; n<wh->num_coins; n++)
    380       {
    381         struct TALER_EXCHANGE_WithdrawCoinPrivateDetails *coin = &details[n];
    382 
    383         TALER_denom_sig_free (&coin->denom_sig);
    384       }
    385       break;
    386     }
    387   case MHD_HTTP_CREATED:
    388     resp.details.created  = wbr->details.created;
    389     break;
    390 
    391   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
    392     resp.details.unavailable_for_legal_reasons =
    393       wbr->details.unavailable_for_legal_reasons;
    394     break;
    395 
    396   default:
    397     /* nothing to do here, .hr.ec and .hr.hint are all set already from previous response */
    398     break;
    399   }
    400   if (NULL != wh->callback)
    401   {
    402     wh->callback (
    403       wh->callback_cls,
    404       &resp);
    405     wh->callback = NULL;
    406   }
    407   TALER_EXCHANGE_post_withdraw_cancel (wh);
    408 }
    409 
    410 
    411 /**
    412  * @brief Callback to copy the results from the call to post_withdraw_blinded
    413  * in the age-restricted case.
    414  *
    415  * @param cls struct TALER_EXCHANGE_PostWithdrawHandle
    416  * @param wbr The response
    417  */
    418 static void
    419 copy_results_with_age_proof (
    420   void *cls,
    421   const struct TALER_EXCHANGE_PostWithdrawBlindedResponse *wbr)
    422 {
    423   /* The original handle from the top-level call to withdraw */
    424   struct TALER_EXCHANGE_PostWithdrawHandle *wh = cls;
    425   uint8_t k =  wbr->details.created.noreveal_index;
    426   struct TALER_EXCHANGE_WithdrawCoinPrivateDetails details[wh->num_coins];
    427   struct TALER_EXCHANGE_PostWithdrawResponse resp = {
    428     .hr = wbr->hr,
    429   };
    430 
    431   wh->withdraw_blinded_handle = NULL;
    432   switch (wbr->hr.http_status)
    433   {
    434   case MHD_HTTP_OK:
    435     /* in the age-restricted case, this should not happen */
    436     GNUNET_break_op (0);
    437     break;
    438 
    439   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
    440     resp.details.unavailable_for_legal_reasons =
    441       wbr->details.unavailable_for_legal_reasons;
    442     break;
    443 
    444   case MHD_HTTP_CREATED:
    445     {
    446       GNUNET_assert (wh->num_coins == wbr->details.created.num_coins);
    447       resp.details.created = wbr->details.created;
    448       resp.details.created.coin_details = details;
    449       resp.details.created.kappa_seed = wh->kappa_seed;
    450       memset (details,
    451               0,
    452               sizeof(details));
    453       for (size_t n = 0; n< wh->num_coins; n++)
    454       {
    455         details[n] = wh->coin_data[n].candidates[k].details;
    456         details[n].planchet = wh->coin_data[n].planchet_details[k];
    457       }
    458       break;
    459     }
    460 
    461   default:
    462     break;
    463   }
    464 
    465   wh->callback (
    466     wh->callback_cls,
    467     &resp);
    468   wh->callback = NULL;
    469   TALER_EXCHANGE_post_withdraw_cancel (wh);
    470 }
    471 
    472 
    473 /**
    474  * @brief Prepares and starts the actual TALER_EXCHANGE_post_withdraw_blinded
    475  * operation once all blinding-prepare steps are done (or immediately if
    476  * there are no CS denominations).
    477  *
    478  * @param wh The withdraw handle
    479  * @return #TALER_EC_NONE on success, error code on failure
    480  */
    481 static enum TALER_ErrorCode
    482 call_withdraw_blinded (
    483   struct TALER_EXCHANGE_PostWithdrawHandle *wh)
    484 {
    485   enum TALER_ErrorCode ec;
    486 
    487   GNUNET_assert (NULL == wh->blinding_prepare_handle);
    488 
    489   if (! wh->with_age_proof)
    490   {
    491     struct TALER_EXCHANGE_WithdrawBlindedCoinInput input[wh->num_coins];
    492 
    493     memset (input,
    494             0,
    495             sizeof(input));
    496 
    497     /* Prepare the blinded planchets as input */
    498     for (size_t n = 0; n < wh->num_coins; n++)
    499     {
    500       input[n].denom_pub =
    501         &wh->coin_data[n].denom_pub;
    502       input[n].planchet_details =
    503         *wh->coin_data[n].planchet_details;
    504     }
    505 
    506     wh->withdraw_blinded_handle =
    507       TALER_EXCHANGE_post_withdraw_blinded_create (
    508         wh->curl_ctx,
    509         wh->keys,
    510         wh->exchange_url,
    511         wh->reserve_priv,
    512         wh->has_blinding_seed ? &wh->blinding_seed : NULL,
    513         wh->num_coins,
    514         input);
    515     if (NULL == wh->withdraw_blinded_handle)
    516       return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    517     ec = TALER_EXCHANGE_post_withdraw_blinded_start (
    518       wh->withdraw_blinded_handle,
    519       &copy_results,
    520       wh);
    521     if (TALER_EC_NONE != ec)
    522     {
    523       wh->withdraw_blinded_handle = NULL;
    524       return ec;
    525     }
    526   }
    527   else
    528   {  /* age restricted case */
    529     struct TALER_EXCHANGE_WithdrawBlindedAgeRestrictedCoinInput
    530       ari[wh->num_coins];
    531 
    532     memset (ari,
    533             0,
    534             sizeof(ari));
    535 
    536     /* Prepare the blinded planchets as input */
    537     for (size_t n = 0; n < wh->num_coins; n++)
    538     {
    539       ari[n].denom_pub = &wh->coin_data[n].denom_pub;
    540       for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
    541         ari[n].planchet_details[k] =
    542           wh->coin_data[n].planchet_details[k];
    543     }
    544 
    545     wh->withdraw_blinded_handle =
    546       TALER_EXCHANGE_post_withdraw_blinded_create (
    547         wh->curl_ctx,
    548         wh->keys,
    549         wh->exchange_url,
    550         wh->reserve_priv,
    551         wh->has_blinding_seed ? &wh->blinding_seed : NULL,
    552         wh->num_coins,
    553         NULL);
    554     if (NULL == wh->withdraw_blinded_handle)
    555       return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    556     TALER_EXCHANGE_post_withdraw_blinded_set_options (
    557       wh->withdraw_blinded_handle,
    558       TALER_EXCHANGE_post_withdraw_blinded_option_with_age_proof (
    559         wh->max_age,
    560         ari));
    561     ec = TALER_EXCHANGE_post_withdraw_blinded_start (
    562       wh->withdraw_blinded_handle,
    563       &copy_results_with_age_proof,
    564       wh);
    565     if (TALER_EC_NONE != ec)
    566     {
    567       TALER_EXCHANGE_post_withdraw_blinded_cancel (wh->withdraw_blinded_handle);
    568       wh->withdraw_blinded_handle = NULL;
    569       return ec;
    570     }
    571   }
    572   return TALER_EC_NONE;
    573 }
    574 
    575 
    576 /**
    577  * @brief Function called when /blinding-prepare is finished.
    578  *
    579  * @param cls the `struct TALER_EXCHANGE_PostWithdrawHandle *`
    580  * @param bpr replies from the /blinding-prepare request
    581  */
    582 static void
    583 blinding_prepare_done (
    584   void *cls,
    585   const struct TALER_EXCHANGE_PostBlindingPrepareResponse *bpr)
    586 {
    587   struct TALER_EXCHANGE_PostWithdrawHandle *wh = cls;
    588 
    589   wh->blinding_prepare_handle = NULL;
    590   switch (bpr->hr.http_status)
    591   {
    592   case MHD_HTTP_OK:
    593     {
    594       bool success = false;
    595       size_t num = bpr->details.ok.num_blinding_values;
    596 
    597       GNUNET_assert (0 != num);
    598       GNUNET_assert (num == wh->num_bp_nonces);
    599       for (size_t i = 0; i < wh->num_bp_coins; i++)
    600       {
    601         struct TALER_PlanchetDetail *planchet = wh->bp_coins[i].planchet;
    602         struct CoinCandidate *can = wh->bp_coins[i].candidate;
    603         size_t cs_idx = wh->bp_coins[i].cs_idx;
    604 
    605         GNUNET_assert (NULL != can);
    606         GNUNET_assert (NULL != planchet);
    607         success = false;
    608 
    609         /* Complete the initialization of the coin with CS denomination */
    610         TALER_denom_ewv_copy (
    611           &can->details.blinding_values,
    612           &bpr->details.ok.blinding_values[cs_idx]);
    613 
    614         GNUNET_assert (GNUNET_CRYPTO_BSA_CS ==
    615                        can->details.blinding_values.blinding_inputs->cipher);
    616 
    617         TALER_planchet_setup_coin_priv (
    618           &can->details.secret,
    619           &can->details.blinding_values,
    620           &can->details.coin_priv);
    621 
    622         TALER_planchet_blinding_secret_create (
    623           &can->details.secret,
    624           &can->details.blinding_values,
    625           &can->details.blinding_key);
    626 
    627         /* This initializes the 2nd half of the
    628            can->planchet_detail.blinded_planchet */
    629         if (GNUNET_OK !=
    630             TALER_planchet_prepare (
    631               wh->bp_coins[i].denom_pub,
    632               &can->details.blinding_values,
    633               &can->details.blinding_key,
    634               &wh->bp_nonces[cs_idx],
    635               &can->details.coin_priv,
    636               &can->details.h_age_commitment,
    637               &can->details.h_coin_pub,
    638               planchet))
    639         {
    640           GNUNET_break (0);
    641           break;
    642         }
    643 
    644         TALER_coin_ev_hash (&planchet->blinded_planchet,
    645                             &planchet->denom_pub_hash,
    646                             &can->blinded_coin_h);
    647         success = true;
    648       }
    649 
    650       /* /blinding-prepare is done, we can now perform the
    651        * actual withdraw operation */
    652       if (success)
    653       {
    654         enum TALER_ErrorCode ec = call_withdraw_blinded (wh);
    655 
    656         if (TALER_EC_NONE != ec)
    657         {
    658           struct TALER_EXCHANGE_PostWithdrawResponse resp = {
    659             .hr.ec = ec,
    660             .hr.http_status = 0,
    661           };
    662 
    663           wh->callback (
    664             wh->callback_cls,
    665             &resp);
    666           wh->callback = NULL;
    667           TALER_EXCHANGE_post_withdraw_cancel (wh);
    668         }
    669         return;
    670       }
    671       else
    672       {
    673         /* prepare completed but coin setup failed */
    674         struct TALER_EXCHANGE_PostWithdrawResponse resp = {
    675           .hr.ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    676           .hr.http_status = 0,
    677         };
    678 
    679         wh->callback (
    680           wh->callback_cls,
    681           &resp);
    682         wh->callback = NULL;
    683         TALER_EXCHANGE_post_withdraw_cancel (wh);
    684         return;
    685       }
    686     }
    687   default:
    688     {
    689       /* We got an error condition during blinding prepare that we need to report */
    690       struct TALER_EXCHANGE_PostWithdrawResponse resp = {
    691         .hr = bpr->hr
    692       };
    693 
    694       wh->callback (
    695         wh->callback_cls,
    696         &resp);
    697       wh->callback = NULL;
    698       break;
    699     }
    700   }
    701   TALER_EXCHANGE_post_withdraw_cancel (wh);
    702 }
    703 
    704 
    705 /**
    706  * @brief Prepares coins for the call to withdraw:
    707  * Performs synchronous crypto for RSA denominations, and stores
    708  * the data needed for the async /blinding-prepare step for CS denominations.
    709  * Does NOT start any async operations.
    710  *
    711  * @param wh The handler to the withdraw
    712  * @param num_coins Number of coins to withdraw
    713  * @param max_age The maximum age to commit to
    714  * @param denoms_pub Array @e num_coins of denominations
    715  * @param seed master seed from which to derive @e num_coins secrets
    716  * @param blinding_seed master seed for the blinding. Might be NULL, in which
    717  *        case the blinding_seed is derived from @e seed
    718  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
    719  */
    720 static enum GNUNET_GenericReturnValue
    721 prepare_coins (
    722   struct TALER_EXCHANGE_PostWithdrawHandle *wh,
    723   size_t num_coins,
    724   uint8_t max_age,
    725   const struct TALER_EXCHANGE_DenomPublicKey *denoms_pub,
    726   const struct TALER_WithdrawMasterSeedP *seed,
    727   const struct TALER_BlindingMasterSeedP *blinding_seed)
    728 {
    729   size_t cs_num = 0;
    730   uint8_t kappa;
    731 
    732 #define FAIL_IF(cond) \
    733         do \
    734         { \
    735           if ((cond)) \
    736           { \
    737             GNUNET_break (! (cond)); \
    738             goto ERROR; \
    739           } \
    740         } while (0)
    741 
    742   GNUNET_assert (0 < num_coins);
    743 
    744   wh->num_coins = num_coins;
    745   wh->max_age = max_age;
    746   wh->age_mask = denoms_pub[0].key.age_mask;
    747   wh->coin_data = GNUNET_new_array (
    748     wh->num_coins,
    749     struct CoinData);
    750 
    751   /* First, figure out how many Clause-Schnorr denominations we have */
    752   for (size_t i =0; i< wh->num_coins; i++)
    753   {
    754     if (GNUNET_CRYPTO_BSA_CS ==
    755         denoms_pub[i].key.bsign_pub_key->cipher)
    756       cs_num++;
    757   }
    758 
    759   if (wh->with_age_proof)
    760     kappa = TALER_CNC_KAPPA;
    761   else
    762     kappa = 1;
    763 
    764   {
    765     struct TALER_PlanchetMasterSecretP secrets[kappa][num_coins];
    766     struct TALER_EXCHANGE_NonceKey cs_nonce_keys[GNUNET_NZL (cs_num)];
    767     uint32_t cs_indices[GNUNET_NZL (cs_num)];
    768 
    769     size_t cs_denom_idx = 0;
    770     size_t cs_coin_idx = 0;
    771 
    772     if (wh->with_age_proof)
    773     {
    774       TALER_withdraw_expand_kappa_seed (seed,
    775                                         &wh->kappa_seed);
    776       for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
    777       {
    778         TALER_withdraw_expand_secrets (
    779           num_coins,
    780           &wh->kappa_seed.tuple[k],
    781           secrets[k]);
    782       }
    783     }
    784     else
    785     {
    786       TALER_withdraw_expand_secrets (
    787         num_coins,
    788         seed,
    789         secrets[0]);
    790     }
    791 
    792     if (0 < cs_num)
    793     {
    794       memset (cs_nonce_keys,
    795               0,
    796               sizeof(cs_nonce_keys));
    797       wh->num_bp_coins = cs_num * kappa;
    798       GNUNET_assert ((1 == kappa) || (cs_num * kappa > cs_num));
    799       wh->bp_coins =
    800         GNUNET_new_array (wh->num_bp_coins,
    801                           struct BlindingPrepareCoinData);
    802       wh->num_bp_nonces = cs_num;
    803       wh->bp_nonces =
    804         GNUNET_new_array (wh->num_bp_nonces,
    805                           union GNUNET_CRYPTO_BlindSessionNonce);
    806       wh->num_bp_nonce_keys = cs_num;
    807       wh->bp_nonce_keys =
    808         GNUNET_new_array (wh->num_bp_nonce_keys,
    809                           struct TALER_EXCHANGE_NonceKey);
    810     }
    811 
    812     for (uint32_t i = 0; i < wh->num_coins; i++)
    813     {
    814       struct CoinData *cd = &wh->coin_data[i];
    815       bool age_denom = (0 != denoms_pub[i].key.age_mask.bits);
    816 
    817       cd->denom_pub = denoms_pub[i];
    818       /* The age mask must be the same for all coins */
    819       FAIL_IF (wh->with_age_proof &&
    820                (0 ==  denoms_pub[i].key.age_mask.bits));
    821       FAIL_IF (wh->age_mask.bits !=
    822                denoms_pub[i].key.age_mask.bits);
    823       TALER_denom_pub_copy (&cd->denom_pub.key,
    824                             &denoms_pub[i].key);
    825 
    826       /* Mark the indices of the coins which are of type Clause-Schnorr
    827        * and add their denomination public key hash to the list.
    828        */
    829       if (GNUNET_CRYPTO_BSA_CS ==
    830           cd->denom_pub.key.bsign_pub_key->cipher)
    831       {
    832         GNUNET_assert (cs_denom_idx < cs_num);
    833         cs_indices[cs_denom_idx] = i;
    834         cs_nonce_keys[cs_denom_idx].cnc_num = i;
    835         cs_nonce_keys[cs_denom_idx].pk = &cd->denom_pub;
    836         wh->bp_nonce_keys[cs_denom_idx].cnc_num = i;
    837         wh->bp_nonce_keys[cs_denom_idx].pk = &cd->denom_pub;
    838         cs_denom_idx++;
    839       }
    840 
    841       /*
    842        * Note that we "loop" here either only once (if with_age_proof is false),
    843        * or TALER_CNC_KAPPA times.
    844        */
    845       for (uint8_t k = 0; k < kappa; k++)
    846       {
    847         struct CoinCandidate *can = &cd->candidates[k];
    848         struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k];
    849 
    850         can->details.secret = secrets[k][i];
    851         /*
    852          * The age restriction needs to be set on a coin if the denomination
    853          * support age restriction. Note that this is regardless of whether
    854          * with_age_proof is set or not.
    855          */
    856         if (age_denom)
    857         {
    858           /* Derive the age restriction from the given secret and
    859            * the maximum age */
    860           TALER_age_restriction_from_secret (
    861             &can->details.secret,
    862             &wh->age_mask,
    863             wh->max_age,
    864             &can->details.age_commitment_proof);
    865 
    866           TALER_age_commitment_hash (
    867             &can->details.age_commitment_proof.commitment,
    868             &can->details.h_age_commitment);
    869         }
    870 
    871         switch (cd->denom_pub.key.bsign_pub_key->cipher)
    872         {
    873         case GNUNET_CRYPTO_BSA_RSA:
    874           TALER_denom_ewv_copy (&can->details.blinding_values,
    875                                 TALER_denom_ewv_rsa_singleton ());
    876           TALER_planchet_setup_coin_priv (&can->details.secret,
    877                                           &can->details.blinding_values,
    878                                           &can->details.coin_priv);
    879           TALER_planchet_blinding_secret_create (&can->details.secret,
    880                                                  &can->details.blinding_values,
    881                                                  &can->details.blinding_key);
    882           FAIL_IF (GNUNET_OK !=
    883                    TALER_planchet_prepare (&cd->denom_pub.key,
    884                                            &can->details.blinding_values,
    885                                            &can->details.blinding_key,
    886                                            NULL,
    887                                            &can->details.coin_priv,
    888                                            (age_denom)
    889                                            ? &can->details.h_age_commitment
    890                                            : NULL,
    891                                            &can->details.h_coin_pub,
    892                                            planchet));
    893           TALER_coin_ev_hash (&planchet->blinded_planchet,
    894                               &planchet->denom_pub_hash,
    895                               &can->blinded_coin_h);
    896           break;
    897 
    898         case GNUNET_CRYPTO_BSA_CS:
    899           {
    900             /* Prepare the nonce and save the index and the denomination for
    901              * the callback after the call to blinding-prepare */
    902             wh->bp_coins[cs_coin_idx].candidate = can;
    903             wh->bp_coins[cs_coin_idx].planchet = planchet;
    904             wh->bp_coins[cs_coin_idx].denom_pub = &cd->denom_pub.key;
    905             wh->bp_coins[cs_coin_idx].cs_idx = i;
    906             wh->bp_coins[cs_coin_idx].age_denom = age_denom;
    907             cs_coin_idx++;
    908             break;
    909           }
    910         default:
    911           FAIL_IF (1);
    912         }
    913       }
    914     }
    915 
    916     if (0 < cs_num)
    917     {
    918       if (wh->options.has_blinding_seed)
    919       {
    920         wh->blinding_seed = wh->options.blinding_seed;
    921       }
    922       else
    923       {
    924         TALER_cs_withdraw_seed_to_blinding_seed (
    925           seed,
    926           &wh->blinding_seed);
    927       }
    928       wh->has_blinding_seed = true;
    929 
    930       TALER_cs_derive_only_cs_blind_nonces_from_seed (
    931         &wh->blinding_seed,
    932         false, /* not for melt */
    933         cs_num,
    934         cs_indices,
    935         wh->bp_nonces);
    936     }
    937   }
    938   return GNUNET_OK;
    939 
    940 ERROR:
    941   if (0 < cs_num)
    942   {
    943     GNUNET_free (wh->bp_nonces);
    944     GNUNET_free (wh->bp_coins);
    945     GNUNET_free (wh->bp_nonce_keys);
    946     wh->num_bp_coins = 0;
    947     wh->num_bp_nonces = 0;
    948     wh->num_bp_nonce_keys = 0;
    949   }
    950   return GNUNET_SYSERR;
    951 #undef FAIL_IF
    952 }
    953 
    954 
    955 struct TALER_EXCHANGE_PostWithdrawHandle *
    956 TALER_EXCHANGE_post_withdraw_create (
    957   struct GNUNET_CURL_Context *curl_ctx,
    958   const char *exchange_url,
    959   struct TALER_EXCHANGE_Keys *keys,
    960   const struct TALER_ReservePrivateKeyP *reserve_priv,
    961   size_t num_coins,
    962   const struct TALER_EXCHANGE_DenomPublicKey denoms_pub[static num_coins],
    963   const struct TALER_WithdrawMasterSeedP *seed,
    964   uint8_t opaque_max_age)
    965 {
    966   struct TALER_EXCHANGE_PostWithdrawHandle *wh;
    967 
    968   wh = GNUNET_new (struct TALER_EXCHANGE_PostWithdrawHandle);
    969   wh->exchange_url = exchange_url;
    970   wh->keys = TALER_EXCHANGE_keys_incref (keys);
    971   wh->curl_ctx = curl_ctx;
    972   wh->reserve_priv = reserve_priv;
    973   wh->seed = *seed;
    974   wh->max_age = opaque_max_age;
    975   wh->init_num_coins = num_coins;
    976   wh->init_denoms_pub = GNUNET_new_array (num_coins,
    977                                           struct TALER_EXCHANGE_DenomPublicKey);
    978   for (size_t i = 0; i < num_coins; i++)
    979   {
    980     wh->init_denoms_pub[i] = denoms_pub[i];
    981     TALER_denom_pub_copy (&wh->init_denoms_pub[i].key,
    982                           &denoms_pub[i].key);
    983   }
    984 
    985   return wh;
    986 }
    987 
    988 
    989 enum GNUNET_GenericReturnValue
    990 TALER_EXCHANGE_post_withdraw_set_options_ (
    991   struct TALER_EXCHANGE_PostWithdrawHandle *pwh,
    992   unsigned int num_options,
    993   const struct TALER_EXCHANGE_PostWithdrawOptionValue options[])
    994 {
    995   for (unsigned int i = 0; i < num_options; i++)
    996   {
    997     const struct TALER_EXCHANGE_PostWithdrawOptionValue *opt = &options[i];
    998     switch (opt->option)
    999     {
   1000     case TALER_EXCHANGE_POST_WITHDRAW_OPTION_END:
   1001       return GNUNET_OK;
   1002     case TALER_EXCHANGE_POST_WITHDRAW_OPTION_WITH_AGE_PROOF:
   1003       pwh->with_age_proof = true;
   1004       pwh->max_age = opt->details.max_age;
   1005       break;
   1006     case TALER_EXCHANGE_POST_WITHDRAW_OPTION_BLINDING_SEED:
   1007       pwh->options.has_blinding_seed = true;
   1008       pwh->options.blinding_seed = opt->details.blinding_seed;
   1009       break;
   1010     }
   1011   }
   1012   return GNUNET_OK;
   1013 }
   1014 
   1015 
   1016 enum TALER_ErrorCode
   1017 TALER_EXCHANGE_post_withdraw_start (
   1018   struct TALER_EXCHANGE_PostWithdrawHandle *pwh,
   1019   TALER_EXCHANGE_PostWithdrawCallback cb,
   1020   TALER_EXCHANGE_POST_WITHDRAW_RESULT_CLOSURE *cb_cls)
   1021 {
   1022   pwh->callback = cb;
   1023   pwh->callback_cls = cb_cls;
   1024 
   1025   /* Run prepare_coins now that options have been applied */
   1026   if (GNUNET_OK !=
   1027       prepare_coins (pwh,
   1028                      pwh->init_num_coins,
   1029                      pwh->max_age,
   1030                      pwh->init_denoms_pub,
   1031                      &pwh->seed,
   1032                      pwh->has_blinding_seed
   1033                      ? &pwh->blinding_seed
   1034                      : NULL))
   1035   {
   1036     GNUNET_free (pwh->coin_data);
   1037     for (size_t i = 0; i < pwh->init_num_coins; i++)
   1038       TALER_denom_pub_free (&pwh->init_denoms_pub[i].key);
   1039     GNUNET_free (pwh->init_denoms_pub);
   1040     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
   1041   }
   1042   /* Free init data - no longer needed after prepare_coins */
   1043   for (size_t i = 0; i < pwh->init_num_coins; i++)
   1044     TALER_denom_pub_free (&pwh->init_denoms_pub[i].key);
   1045   GNUNET_free (pwh->init_denoms_pub);
   1046 
   1047   if (0 < pwh->num_bp_coins)
   1048   {
   1049     /* There are CS denominations; start the blinding-prepare request */
   1050     pwh->blinding_prepare_handle =
   1051       TALER_EXCHANGE_post_blinding_prepare_for_withdraw_create (
   1052         pwh->curl_ctx,
   1053         pwh->exchange_url,
   1054         &pwh->blinding_seed,
   1055         pwh->num_bp_nonce_keys,
   1056         pwh->bp_nonce_keys);
   1057     if (NULL == pwh->blinding_prepare_handle)
   1058       return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
   1059     {
   1060       enum TALER_ErrorCode ec =
   1061         TALER_EXCHANGE_post_blinding_prepare_start (
   1062           pwh->blinding_prepare_handle,
   1063           &blinding_prepare_done,
   1064           pwh);
   1065       if (TALER_EC_NONE != ec)
   1066       {
   1067         pwh->blinding_prepare_handle = NULL;
   1068         return ec;
   1069       }
   1070     }
   1071     return TALER_EC_NONE;
   1072   }
   1073 
   1074   /* No CS denominations; proceed directly to the withdraw protocol */
   1075   return call_withdraw_blinded (pwh);
   1076 }
   1077 
   1078 
   1079 void
   1080 TALER_EXCHANGE_post_withdraw_cancel (
   1081   struct TALER_EXCHANGE_PostWithdrawHandle *wh)
   1082 {
   1083   uint8_t kappa = wh->with_age_proof ? TALER_CNC_KAPPA : 1;
   1084 
   1085   /* Cleanup init data if _start was never called (or failed) */
   1086   if (NULL != wh->init_denoms_pub)
   1087   {
   1088     for (size_t i = 0; i < wh->init_num_coins; i++)
   1089       TALER_denom_pub_free (&wh->init_denoms_pub[i].key);
   1090     GNUNET_free (wh->init_denoms_pub);
   1091   }
   1092   /* Cleanup coin data */
   1093   if (NULL != wh->coin_data)
   1094   {
   1095     for (unsigned int i = 0; i < wh->num_coins; i++)
   1096     {
   1097       struct CoinData *cd = &wh->coin_data[i];
   1098 
   1099       for (uint8_t k = 0; k < kappa; k++)
   1100       {
   1101         struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k];
   1102         struct CoinCandidate *can = &cd->candidates[k];
   1103 
   1104         TALER_blinded_planchet_free (&planchet->blinded_planchet);
   1105         TALER_denom_ewv_free (&can->details.blinding_values);
   1106         TALER_age_commitment_proof_free (&can->details.age_commitment_proof);
   1107       }
   1108       TALER_denom_pub_free (&cd->denom_pub.key);
   1109     }
   1110   }
   1111 
   1112   TALER_EXCHANGE_post_blinding_prepare_cancel (wh->blinding_prepare_handle);
   1113   wh->blinding_prepare_handle = NULL;
   1114   TALER_EXCHANGE_post_withdraw_blinded_cancel (wh->withdraw_blinded_handle);
   1115   wh->withdraw_blinded_handle = NULL;
   1116 
   1117   GNUNET_free (wh->bp_coins);
   1118   GNUNET_free (wh->bp_nonces);
   1119   GNUNET_free (wh->bp_nonce_keys);
   1120   GNUNET_free (wh->coin_data);
   1121   TALER_EXCHANGE_keys_decref (wh->keys);
   1122   GNUNET_free (wh);
   1123 }
   1124 
   1125 
   1126 /* exchange_api_post-withdraw.c */