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 (32846B)


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