LCOV - code coverage report
Current view: top level - lib - exchange_api_post-withdraw.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 83.7 % 375 314
Test Date: 2026-04-14 15:39:31 Functions: 100.0 % 9 9

            Line data    Source code
       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           70 : 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           70 :   struct TALER_EXCHANGE_PostWithdrawHandle *wh = cls;
     311           70 :   struct TALER_EXCHANGE_PostWithdrawResponse resp = {
     312              :     .hr = wbr->hr,
     313              :   };
     314              : 
     315           70 :   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           70 :   switch (wbr->hr.http_status)
     323              :   {
     324           61 :   case MHD_HTTP_OK:
     325           61 :     {
     326              :       struct TALER_EXCHANGE_WithdrawCoinPrivateDetails
     327           61 :         details[GNUNET_NZL (wh->num_coins)];
     328           61 :       bool ok = true;
     329              : 
     330           61 :       GNUNET_assert (wh->num_coins == wbr->details.ok.num_sigs);
     331           61 :       memset (details,
     332              :               0,
     333              :               sizeof(details));
     334           61 :       resp.details.ok.num_sigs = wbr->details.ok.num_sigs;
     335           61 :       resp.details.ok.coin_details = details;
     336           61 :       resp.details.ok.planchets_h = wbr->details.ok.planchets_h;
     337          124 :       for (size_t n = 0; n<wh->num_coins; n++)
     338              :       {
     339           63 :         const struct TALER_BlindedDenominationSignature *bsig =
     340           63 :           &wbr->details.ok.blinded_denom_sigs[n];
     341           63 :         struct CoinData *cd = &wh->coin_data[n];
     342           63 :         struct TALER_EXCHANGE_WithdrawCoinPrivateDetails *coin = &details[n];
     343              :         struct TALER_FreshCoin fresh_coin;
     344              : 
     345           63 :         *coin = wh->coin_data[n].candidates[0].details;
     346           63 :         coin->planchet = wh->coin_data[n].planchet_details[0];
     347           63 :         GNUNET_CRYPTO_eddsa_key_get_public (
     348           63 :           &coin->coin_priv.eddsa_priv,
     349              :           &coin->coin_pub.eddsa_pub);
     350              : 
     351           63 :         if (GNUNET_OK !=
     352           63 :             TALER_planchet_to_coin (&cd->denom_pub.key,
     353              :                                     bsig,
     354           63 :                                     &coin->blinding_key,
     355           63 :                                     &coin->coin_priv,
     356           63 :                                     &coin->h_age_commitment,
     357           63 :                                     &coin->h_coin_pub,
     358           63 :                                     &coin->blinding_values,
     359              :                                     &fresh_coin))
     360              :         {
     361            0 :           resp.hr.http_status = 0;
     362            0 :           resp.hr.ec = TALER_EC_EXCHANGE_WITHDRAW_UNBLIND_FAILURE;
     363            0 :           GNUNET_break_op (0);
     364            0 :           ok = false;
     365            0 :           break;
     366              :         }
     367           63 :         coin->denom_sig = fresh_coin.sig;
     368              :       }
     369           61 :       if (ok)
     370              :       {
     371           61 :         wh->callback (
     372              :           wh->callback_cls,
     373              :           &resp);
     374           61 :         wh->callback = NULL;
     375              :       }
     376          124 :       for (size_t n = 0; n<wh->num_coins; n++)
     377              :       {
     378           63 :         struct TALER_EXCHANGE_WithdrawCoinPrivateDetails *coin = &details[n];
     379              : 
     380           63 :         TALER_denom_sig_free (&coin->denom_sig);
     381              :       }
     382           61 :       break;
     383              :     }
     384            0 :   case MHD_HTTP_CREATED:
     385            0 :     resp.details.created = wbr->details.created;
     386            0 :     break;
     387            4 :   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
     388            4 :     resp.details.unavailable_for_legal_reasons =
     389              :       wbr->details.unavailable_for_legal_reasons;
     390            4 :     break;
     391              : 
     392            5 :   default:
     393              :     /* nothing to do here, .hr.ec and .hr.hint are all set already from previous response */
     394            5 :     break;
     395              :   }
     396           70 :   if (NULL != wh->callback)
     397              :   {
     398            9 :     wh->callback (
     399              :       wh->callback_cls,
     400              :       &resp);
     401            9 :     wh->callback = NULL;
     402              :   }
     403           70 :   TALER_EXCHANGE_post_withdraw_cancel (wh);
     404           70 : }
     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            5 : copy_results_with_age_proof (
     416              :   void *cls,
     417              :   const struct TALER_EXCHANGE_PostWithdrawBlindedResponse *wbr)
     418            5 : {
     419              :   /* The original handle from the top-level call to withdraw */
     420            5 :   struct TALER_EXCHANGE_PostWithdrawHandle *wh = cls;
     421            5 :   uint8_t k =  wbr->details.created.noreveal_index;
     422            5 :   struct TALER_EXCHANGE_WithdrawCoinPrivateDetails details[wh->num_coins];
     423            5 :   struct TALER_EXCHANGE_PostWithdrawResponse resp = {
     424              :     .hr = wbr->hr,
     425              :   };
     426              : 
     427            5 :   wh->withdraw_blinded_handle = NULL;
     428            5 :   switch (wbr->hr.http_status)
     429              :   {
     430            0 :   case MHD_HTTP_OK:
     431              :     /* in the age-restricted case, this should not happen */
     432            0 :     GNUNET_break_op (0);
     433            0 :     break;
     434            3 :   case MHD_HTTP_CREATED:
     435              :     {
     436            3 :       GNUNET_assert (wh->num_coins == wbr->details.created.num_coins);
     437            3 :       resp.details.created = wbr->details.created;
     438            3 :       resp.details.created.coin_details = details;
     439            3 :       resp.details.created.kappa_seed = wh->kappa_seed;
     440            3 :       memset (details,
     441              :               0,
     442              :               sizeof(details));
     443           10 :       for (size_t n = 0; n< wh->num_coins; n++)
     444              :       {
     445            7 :         details[n] = wh->coin_data[n].candidates[k].details;
     446            7 :         details[n].planchet = wh->coin_data[n].planchet_details[k];
     447              :       }
     448            3 :       break;
     449              :     }
     450            0 :   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
     451            0 :     resp.details.unavailable_for_legal_reasons =
     452              :       wbr->details.unavailable_for_legal_reasons;
     453            0 :     break;
     454            2 :   default:
     455            2 :     break;
     456              :   }
     457              : 
     458            5 :   wh->callback (
     459              :     wh->callback_cls,
     460              :     &resp);
     461            5 :   wh->callback = NULL;
     462            5 :   TALER_EXCHANGE_post_withdraw_cancel (wh);
     463            5 : }
     464              : 
     465              : 
     466              : /**
     467              :  * @brief Prepares and starts the actual TALER_EXCHANGE_post_withdraw_blinded
     468              :  * operation once all blinding-prepare steps are done (or immediately if
     469              :  * there are no CS denominations).
     470              :  *
     471              :  * @param wh The withdraw handle
     472              :  * @return #TALER_EC_NONE on success, error code on failure
     473              :  */
     474              : static enum TALER_ErrorCode
     475           75 : call_withdraw_blinded (
     476              :   struct TALER_EXCHANGE_PostWithdrawHandle *wh)
     477              : {
     478              :   enum TALER_ErrorCode ec;
     479              : 
     480           75 :   GNUNET_assert (NULL == wh->blinding_prepare_handle);
     481              : 
     482           75 :   if (! wh->with_age_proof)
     483           70 :   {
     484           70 :     struct TALER_EXCHANGE_WithdrawBlindedCoinInput input[wh->num_coins];
     485              : 
     486           70 :     memset (input,
     487              :             0,
     488              :             sizeof(input));
     489              : 
     490              :     /* Prepare the blinded planchets as input */
     491          142 :     for (size_t n = 0; n < wh->num_coins; n++)
     492              :     {
     493           72 :       input[n].denom_pub =
     494           72 :         &wh->coin_data[n].denom_pub;
     495           72 :       input[n].planchet_details =
     496           72 :         *wh->coin_data[n].planchet_details;
     497              :     }
     498              : 
     499           70 :     wh->withdraw_blinded_handle =
     500           70 :       TALER_EXCHANGE_post_withdraw_blinded_create (
     501              :         wh->curl_ctx,
     502              :         wh->keys,
     503              :         wh->exchange_url,
     504              :         wh->reserve_priv,
     505           70 :         wh->has_blinding_seed ? &wh->blinding_seed : NULL,
     506              :         wh->num_coins,
     507              :         input);
     508           70 :     if (NULL == wh->withdraw_blinded_handle)
     509            0 :       return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     510           70 :     ec = TALER_EXCHANGE_post_withdraw_blinded_start (
     511              :       wh->withdraw_blinded_handle,
     512              :       &copy_results,
     513              :       wh);
     514           70 :     if (TALER_EC_NONE != ec)
     515              :     {
     516            0 :       wh->withdraw_blinded_handle = NULL;
     517            0 :       return ec;
     518              :     }
     519              :   }
     520              :   else
     521            5 :   {  /* age restricted case */
     522              :     struct TALER_EXCHANGE_WithdrawBlindedAgeRestrictedCoinInput
     523            5 :       ari[wh->num_coins];
     524              : 
     525            5 :     memset (ari,
     526              :             0,
     527              :             sizeof(ari));
     528              : 
     529              :     /* Prepare the blinded planchets as input */
     530           14 :     for (size_t n = 0; n < wh->num_coins; n++)
     531              :     {
     532            9 :       ari[n].denom_pub = &wh->coin_data[n].denom_pub;
     533           36 :       for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
     534           27 :         ari[n].planchet_details[k] =
     535           27 :           wh->coin_data[n].planchet_details[k];
     536              :     }
     537              : 
     538            5 :     wh->withdraw_blinded_handle =
     539            5 :       TALER_EXCHANGE_post_withdraw_blinded_create (
     540              :         wh->curl_ctx,
     541              :         wh->keys,
     542              :         wh->exchange_url,
     543              :         wh->reserve_priv,
     544            5 :         wh->has_blinding_seed ? &wh->blinding_seed : NULL,
     545              :         wh->num_coins,
     546              :         NULL);
     547            5 :     if (NULL == wh->withdraw_blinded_handle)
     548            0 :       return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     549            5 :     TALER_EXCHANGE_post_withdraw_blinded_set_options (
     550              :       wh->withdraw_blinded_handle,
     551              :       TALER_EXCHANGE_post_withdraw_blinded_option_with_age_proof (
     552              :         wh->max_age,
     553              :         ari));
     554            5 :     ec = TALER_EXCHANGE_post_withdraw_blinded_start (
     555              :       wh->withdraw_blinded_handle,
     556              :       &copy_results_with_age_proof,
     557              :       wh);
     558            5 :     if (TALER_EC_NONE != ec)
     559              :     {
     560            0 :       TALER_EXCHANGE_post_withdraw_blinded_cancel (wh->withdraw_blinded_handle);
     561            0 :       wh->withdraw_blinded_handle = NULL;
     562            0 :       return ec;
     563              :     }
     564              :   }
     565           75 :   return TALER_EC_NONE;
     566              : }
     567              : 
     568              : 
     569              : /**
     570              :  * @brief Function called when /blinding-prepare is finished.
     571              :  *
     572              :  * @param cls the `struct TALER_EXCHANGE_PostWithdrawHandle *`
     573              :  * @param bpr replies from the /blinding-prepare request
     574              :  */
     575              : static void
     576           35 : blinding_prepare_done (
     577              :   void *cls,
     578              :   const struct TALER_EXCHANGE_PostBlindingPrepareResponse *bpr)
     579              : {
     580           35 :   struct TALER_EXCHANGE_PostWithdrawHandle *wh = cls;
     581              : 
     582           35 :   wh->blinding_prepare_handle = NULL;
     583           35 :   switch (bpr->hr.http_status)
     584              :   {
     585           35 :   case MHD_HTTP_OK:
     586              :     {
     587           35 :       bool success = false;
     588           35 :       size_t num = bpr->details.ok.num_blinding_values;
     589              : 
     590           35 :       GNUNET_assert (0 != num);
     591           35 :       GNUNET_assert (num == wh->num_bp_nonces);
     592           81 :       for (size_t i = 0; i < wh->num_bp_coins; i++)
     593              :       {
     594           46 :         struct TALER_PlanchetDetail *planchet = wh->bp_coins[i].planchet;
     595           46 :         struct CoinCandidate *can = wh->bp_coins[i].candidate;
     596           46 :         size_t cs_idx = wh->bp_coins[i].cs_idx;
     597              : 
     598           46 :         GNUNET_assert (NULL != can);
     599           46 :         GNUNET_assert (NULL != planchet);
     600           46 :         success = false;
     601              : 
     602              :         /* Complete the initialization of the coin with CS denomination */
     603           46 :         TALER_denom_ewv_copy (
     604              :           &can->details.blinding_values,
     605           46 :           &bpr->details.ok.blinding_values[cs_idx]);
     606              : 
     607           46 :         GNUNET_assert (GNUNET_CRYPTO_BSA_CS ==
     608              :                        can->details.blinding_values.blinding_inputs->cipher);
     609              : 
     610           46 :         TALER_planchet_setup_coin_priv (
     611           46 :           &can->details.secret,
     612           46 :           &can->details.blinding_values,
     613              :           &can->details.coin_priv);
     614              : 
     615           46 :         TALER_planchet_blinding_secret_create (
     616           46 :           &can->details.secret,
     617           46 :           &can->details.blinding_values,
     618              :           &can->details.blinding_key);
     619              : 
     620              :         /* This initializes the 2nd half of the
     621              :            can->planchet_detail.blinded_planchet */
     622           46 :         if (GNUNET_OK !=
     623           46 :             TALER_planchet_prepare (
     624           46 :               wh->bp_coins[i].denom_pub,
     625           46 :               &can->details.blinding_values,
     626           46 :               &can->details.blinding_key,
     627           46 :               &wh->bp_nonces[cs_idx],
     628           46 :               &can->details.coin_priv,
     629           46 :               &can->details.h_age_commitment,
     630              :               &can->details.h_coin_pub,
     631              :               planchet))
     632              :         {
     633            0 :           GNUNET_break (0);
     634            0 :           break;
     635              :         }
     636              : 
     637           46 :         TALER_coin_ev_hash (&planchet->blinded_planchet,
     638           46 :                             &planchet->denom_pub_hash,
     639              :                             &can->blinded_coin_h);
     640           46 :         success = true;
     641              :       }
     642              : 
     643              :       /* /blinding-prepare is done, we can now perform the
     644              :        * actual withdraw operation */
     645           35 :       if (success)
     646              :       {
     647           35 :         enum TALER_ErrorCode ec = call_withdraw_blinded (wh);
     648              : 
     649           35 :         if (TALER_EC_NONE != ec)
     650              :         {
     651            0 :           struct TALER_EXCHANGE_PostWithdrawResponse resp = {
     652              :             .hr.ec = ec,
     653              :             .hr.http_status = 0,
     654              :           };
     655              : 
     656            0 :           wh->callback (
     657              :             wh->callback_cls,
     658              :             &resp);
     659            0 :           wh->callback = NULL;
     660            0 :           TALER_EXCHANGE_post_withdraw_cancel (wh);
     661              :         }
     662           35 :         return;
     663              :       }
     664              :       else
     665              :       {
     666              :         /* prepare completed but coin setup failed */
     667            0 :         struct TALER_EXCHANGE_PostWithdrawResponse resp = {
     668              :           .hr.ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
     669              :           .hr.http_status = 0,
     670              :         };
     671              : 
     672            0 :         wh->callback (
     673              :           wh->callback_cls,
     674              :           &resp);
     675            0 :         wh->callback = NULL;
     676            0 :         TALER_EXCHANGE_post_withdraw_cancel (wh);
     677            0 :         return;
     678              :       }
     679              :     }
     680            0 :   default:
     681              :     {
     682              :       /* We got an error condition during blinding prepare that we need to report */
     683            0 :       struct TALER_EXCHANGE_PostWithdrawResponse resp = {
     684              :         .hr = bpr->hr
     685              :       };
     686              : 
     687            0 :       wh->callback (
     688              :         wh->callback_cls,
     689              :         &resp);
     690            0 :       wh->callback = NULL;
     691            0 :       break;
     692              :     }
     693              :   }
     694            0 :   TALER_EXCHANGE_post_withdraw_cancel (wh);
     695              : }
     696              : 
     697              : 
     698              : /**
     699              :  * @brief Prepares coins for the call to withdraw:
     700              :  * Performs synchronous crypto for RSA denominations, and stores
     701              :  * the data needed for the async /blinding-prepare step for CS denominations.
     702              :  * Does NOT start any async operations.
     703              :  *
     704              :  * @param wh The handler to the withdraw
     705              :  * @param num_coins Number of coins to withdraw
     706              :  * @param max_age The maximum age to commit to
     707              :  * @param denoms_pub Array @e num_coins of denominations
     708              :  * @param seed master seed from which to derive @e num_coins secrets
     709              :  * @param blinding_seed master seed for the blinding. Might be NULL, in which
     710              :  *        case the blinding_seed is derived from @e seed
     711              :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
     712              :  */
     713              : static enum GNUNET_GenericReturnValue
     714           75 : prepare_coins (
     715              :   struct TALER_EXCHANGE_PostWithdrawHandle *wh,
     716              :   size_t num_coins,
     717              :   uint8_t max_age,
     718              :   const struct TALER_EXCHANGE_DenomPublicKey *denoms_pub,
     719              :   const struct TALER_WithdrawMasterSeedP *seed,
     720              :   const struct TALER_BlindingMasterSeedP *blinding_seed)
     721              : {
     722           75 :   size_t cs_num = 0;
     723              :   uint8_t kappa;
     724              : 
     725              : #define FAIL_IF(cond) \
     726              :         do \
     727              :         { \
     728              :           if ((cond)) \
     729              :           { \
     730              :             GNUNET_break (! (cond)); \
     731              :             goto ERROR; \
     732              :           } \
     733              :         } while (0)
     734              : 
     735           75 :   GNUNET_assert (0 < num_coins);
     736              : 
     737           75 :   wh->num_coins = num_coins;
     738           75 :   wh->max_age = max_age;
     739           75 :   wh->age_mask = denoms_pub[0].key.age_mask;
     740           75 :   wh->coin_data = GNUNET_new_array (
     741              :     wh->num_coins,
     742              :     struct CoinData);
     743              : 
     744              :   /* First, figure out how many Clause-Schnorr denominations we have */
     745          156 :   for (size_t i =0; i< wh->num_coins; i++)
     746              :   {
     747           81 :     if (GNUNET_CRYPTO_BSA_CS ==
     748           81 :         denoms_pub[i].key.bsign_pub_key->cipher)
     749           38 :       cs_num++;
     750              :   }
     751              : 
     752           75 :   if (wh->with_age_proof)
     753            5 :     kappa = TALER_CNC_KAPPA;
     754              :   else
     755           70 :     kappa = 1;
     756              : 
     757           75 :   {
     758           75 :     struct TALER_PlanchetMasterSecretP secrets[kappa][num_coins];
     759           75 :     struct TALER_EXCHANGE_NonceKey cs_nonce_keys[GNUNET_NZL (cs_num)];
     760           75 :     uint32_t cs_indices[GNUNET_NZL (cs_num)];
     761              : 
     762           75 :     size_t cs_denom_idx = 0;
     763           75 :     size_t cs_coin_idx = 0;
     764              : 
     765           75 :     if (wh->with_age_proof)
     766              :     {
     767            5 :       TALER_withdraw_expand_kappa_seed (seed,
     768              :                                         &wh->kappa_seed);
     769           20 :       for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
     770              :       {
     771           15 :         TALER_withdraw_expand_secrets (
     772              :           num_coins,
     773           15 :           &wh->kappa_seed.tuple[k],
     774           15 :           secrets[k]);
     775              :       }
     776              :     }
     777              :     else
     778              :     {
     779           70 :       TALER_withdraw_expand_secrets (
     780              :         num_coins,
     781              :         seed,
     782           70 :         secrets[0]);
     783              :     }
     784              : 
     785           75 :     if (0 < cs_num)
     786              :     {
     787           35 :       memset (cs_nonce_keys,
     788              :               0,
     789              :               sizeof(cs_nonce_keys));
     790           35 :       wh->num_bp_coins = cs_num * kappa;
     791           35 :       GNUNET_assert ((1 == kappa) || (cs_num * kappa > cs_num));
     792           35 :       wh->bp_coins =
     793           35 :         GNUNET_new_array (wh->num_bp_coins,
     794              :                           struct BlindingPrepareCoinData);
     795           35 :       wh->num_bp_nonces = cs_num;
     796           35 :       wh->bp_nonces =
     797           35 :         GNUNET_new_array (wh->num_bp_nonces,
     798              :                           union GNUNET_CRYPTO_BlindSessionNonce);
     799           35 :       wh->num_bp_nonce_keys = cs_num;
     800           35 :       wh->bp_nonce_keys =
     801           35 :         GNUNET_new_array (wh->num_bp_nonce_keys,
     802              :                           struct TALER_EXCHANGE_NonceKey);
     803              :     }
     804              : 
     805          156 :     for (uint32_t i = 0; i < wh->num_coins; i++)
     806              :     {
     807           81 :       struct CoinData *cd = &wh->coin_data[i];
     808           81 :       bool age_denom = (0 != denoms_pub[i].key.age_mask.bits);
     809              : 
     810           81 :       cd->denom_pub = denoms_pub[i];
     811              :       /* The age mask must be the same for all coins */
     812           81 :       FAIL_IF (wh->with_age_proof &&
     813              :                (0 ==  denoms_pub[i].key.age_mask.bits));
     814           81 :       FAIL_IF (wh->age_mask.bits !=
     815              :                denoms_pub[i].key.age_mask.bits);
     816           81 :       TALER_denom_pub_copy (&cd->denom_pub.key,
     817           81 :                             &denoms_pub[i].key);
     818              : 
     819              :       /* Mark the indices of the coins which are of type Clause-Schnorr
     820              :        * and add their denomination public key hash to the list.
     821              :        */
     822           81 :       if (GNUNET_CRYPTO_BSA_CS ==
     823           81 :           cd->denom_pub.key.bsign_pub_key->cipher)
     824              :       {
     825           38 :         GNUNET_assert (cs_denom_idx < cs_num);
     826           38 :         cs_indices[cs_denom_idx] = i;
     827           38 :         cs_nonce_keys[cs_denom_idx].cnc_num = i;
     828           38 :         cs_nonce_keys[cs_denom_idx].pk = &cd->denom_pub;
     829           38 :         wh->bp_nonce_keys[cs_denom_idx].cnc_num = i;
     830           38 :         wh->bp_nonce_keys[cs_denom_idx].pk = &cd->denom_pub;
     831           38 :         cs_denom_idx++;
     832              :       }
     833              : 
     834              :       /*
     835              :        * Note that we "loop" here either only once (if with_age_proof is false),
     836              :        * or TALER_CNC_KAPPA times.
     837              :        */
     838          180 :       for (uint8_t k = 0; k < kappa; k++)
     839              :       {
     840           99 :         struct CoinCandidate *can = &cd->candidates[k];
     841           99 :         struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k];
     842              : 
     843           99 :         can->details.secret = secrets[k][i];
     844              :         /*
     845              :          * The age restriction needs to be set on a coin if the denomination
     846              :          * support age restriction. Note that this is regardless of whether
     847              :          * with_age_proof is set or not.
     848              :          */
     849           99 :         if (age_denom)
     850              :         {
     851              :           /* Derive the age restriction from the given secret and
     852              :            * the maximum age */
     853           37 :           TALER_age_restriction_from_secret (
     854           37 :             &can->details.secret,
     855           37 :             &wh->age_mask,
     856           37 :             wh->max_age,
     857              :             &can->details.age_commitment_proof);
     858              : 
     859           37 :           TALER_age_commitment_hash (
     860           37 :             &can->details.age_commitment_proof.commitment,
     861              :             &can->details.h_age_commitment);
     862              :         }
     863              : 
     864           99 :         switch (cd->denom_pub.key.bsign_pub_key->cipher)
     865              :         {
     866           53 :         case GNUNET_CRYPTO_BSA_RSA:
     867           53 :           TALER_denom_ewv_copy (&can->details.blinding_values,
     868              :                                 TALER_denom_ewv_rsa_singleton ());
     869           53 :           TALER_planchet_setup_coin_priv (&can->details.secret,
     870           53 :                                           &can->details.blinding_values,
     871              :                                           &can->details.coin_priv);
     872           53 :           TALER_planchet_blinding_secret_create (&can->details.secret,
     873           53 :                                                  &can->details.blinding_values,
     874              :                                                  &can->details.blinding_key);
     875           53 :           FAIL_IF (GNUNET_OK !=
     876              :                    TALER_planchet_prepare (&cd->denom_pub.key,
     877              :                                            &can->details.blinding_values,
     878              :                                            &can->details.blinding_key,
     879              :                                            NULL,
     880              :                                            &can->details.coin_priv,
     881              :                                            (age_denom)
     882              :                                            ? &can->details.h_age_commitment
     883              :                                            : NULL,
     884              :                                            &can->details.h_coin_pub,
     885              :                                            planchet));
     886           53 :           TALER_coin_ev_hash (&planchet->blinded_planchet,
     887           53 :                               &planchet->denom_pub_hash,
     888              :                               &can->blinded_coin_h);
     889           53 :           break;
     890              : 
     891           46 :         case GNUNET_CRYPTO_BSA_CS:
     892              :           {
     893              :             /* Prepare the nonce and save the index and the denomination for
     894              :              * the callback after the call to blinding-prepare */
     895           46 :             wh->bp_coins[cs_coin_idx].candidate = can;
     896           46 :             wh->bp_coins[cs_coin_idx].planchet = planchet;
     897           46 :             wh->bp_coins[cs_coin_idx].denom_pub = &cd->denom_pub.key;
     898           46 :             wh->bp_coins[cs_coin_idx].cs_idx = i;
     899           46 :             wh->bp_coins[cs_coin_idx].age_denom = age_denom;
     900           46 :             cs_coin_idx++;
     901           46 :             break;
     902              :           }
     903            0 :         default:
     904            0 :           FAIL_IF (1);
     905              :         }
     906              :       }
     907              :     }
     908              : 
     909           75 :     if (0 < cs_num)
     910              :     {
     911           35 :       if (wh->options.has_blinding_seed)
     912              :       {
     913           32 :         wh->blinding_seed = wh->options.blinding_seed;
     914              :       }
     915              :       else
     916              :       {
     917            3 :         TALER_cs_withdraw_seed_to_blinding_seed (
     918              :           seed,
     919              :           &wh->blinding_seed);
     920              :       }
     921           35 :       wh->has_blinding_seed = true;
     922              : 
     923           35 :       TALER_cs_derive_only_cs_blind_nonces_from_seed (
     924           35 :         &wh->blinding_seed,
     925              :         false, /* not for melt */
     926              :         cs_num,
     927              :         cs_indices,
     928              :         wh->bp_nonces);
     929              :     }
     930              :   }
     931           75 :   return GNUNET_OK;
     932              : 
     933            0 : ERROR:
     934            0 :   if (0 < cs_num)
     935              :   {
     936            0 :     GNUNET_free (wh->bp_nonces);
     937            0 :     GNUNET_free (wh->bp_coins);
     938            0 :     GNUNET_free (wh->bp_nonce_keys);
     939            0 :     wh->num_bp_coins = 0;
     940            0 :     wh->num_bp_nonces = 0;
     941            0 :     wh->num_bp_nonce_keys = 0;
     942              :   }
     943            0 :   return GNUNET_SYSERR;
     944              : #undef FAIL_IF
     945              : }
     946              : 
     947              : 
     948              : struct TALER_EXCHANGE_PostWithdrawHandle *
     949           75 : TALER_EXCHANGE_post_withdraw_create (
     950              :   struct GNUNET_CURL_Context *curl_ctx,
     951              :   const char *exchange_url,
     952              :   struct TALER_EXCHANGE_Keys *keys,
     953              :   const struct TALER_ReservePrivateKeyP *reserve_priv,
     954              :   size_t num_coins,
     955              :   const struct TALER_EXCHANGE_DenomPublicKey denoms_pub[static num_coins],
     956              :   const struct TALER_WithdrawMasterSeedP *seed,
     957              :   uint8_t opaque_max_age)
     958           75 : {
     959              :   struct TALER_EXCHANGE_PostWithdrawHandle *wh;
     960              : 
     961           75 :   wh = GNUNET_new (struct TALER_EXCHANGE_PostWithdrawHandle);
     962           75 :   wh->exchange_url = exchange_url;
     963           75 :   wh->keys = TALER_EXCHANGE_keys_incref (keys);
     964           75 :   wh->curl_ctx = curl_ctx;
     965           75 :   wh->reserve_priv = reserve_priv;
     966           75 :   wh->seed = *seed;
     967           75 :   wh->max_age = opaque_max_age;
     968           75 :   wh->init_num_coins = num_coins;
     969           75 :   wh->init_denoms_pub = GNUNET_new_array (num_coins,
     970              :                                           struct TALER_EXCHANGE_DenomPublicKey);
     971          156 :   for (size_t i = 0; i < num_coins; i++)
     972              :   {
     973           81 :     wh->init_denoms_pub[i] = denoms_pub[i];
     974           81 :     TALER_denom_pub_copy (&wh->init_denoms_pub[i].key,
     975           81 :                           &denoms_pub[i].key);
     976              :   }
     977              : 
     978           75 :   return wh;
     979              : }
     980              : 
     981              : 
     982              : enum GNUNET_GenericReturnValue
     983           73 : TALER_EXCHANGE_post_withdraw_set_options_ (
     984              :   struct TALER_EXCHANGE_PostWithdrawHandle *pwh,
     985              :   unsigned int num_options,
     986              :   const struct TALER_EXCHANGE_PostWithdrawOptionValue options[])
     987              : {
     988          146 :   for (unsigned int i = 0; i < num_options; i++)
     989              :   {
     990          146 :     const struct TALER_EXCHANGE_PostWithdrawOptionValue *opt = &options[i];
     991          146 :     switch (opt->option)
     992              :     {
     993           73 :     case TALER_EXCHANGE_POST_WITHDRAW_OPTION_END:
     994           73 :       return GNUNET_OK;
     995            5 :     case TALER_EXCHANGE_POST_WITHDRAW_OPTION_WITH_AGE_PROOF:
     996            5 :       pwh->with_age_proof = true;
     997            5 :       pwh->max_age = opt->details.max_age;
     998            5 :       break;
     999           68 :     case TALER_EXCHANGE_POST_WITHDRAW_OPTION_BLINDING_SEED:
    1000           68 :       pwh->options.has_blinding_seed = true;
    1001           68 :       pwh->options.blinding_seed = opt->details.blinding_seed;
    1002           68 :       break;
    1003              :     }
    1004              :   }
    1005            0 :   return GNUNET_OK;
    1006              : }
    1007              : 
    1008              : 
    1009              : enum TALER_ErrorCode
    1010           75 : TALER_EXCHANGE_post_withdraw_start (
    1011              :   struct TALER_EXCHANGE_PostWithdrawHandle *pwh,
    1012              :   TALER_EXCHANGE_PostWithdrawCallback cb,
    1013              :   TALER_EXCHANGE_POST_WITHDRAW_RESULT_CLOSURE *cb_cls)
    1014              : {
    1015           75 :   pwh->callback = cb;
    1016           75 :   pwh->callback_cls = cb_cls;
    1017              : 
    1018              :   /* Run prepare_coins now that options have been applied */
    1019           75 :   if (GNUNET_OK !=
    1020           75 :       prepare_coins (pwh,
    1021              :                      pwh->init_num_coins,
    1022           75 :                      pwh->max_age,
    1023           75 :                      pwh->init_denoms_pub,
    1024           75 :                      &pwh->seed,
    1025           75 :                      pwh->has_blinding_seed
    1026              :                      ? &pwh->blinding_seed
    1027              :                      : NULL))
    1028              :   {
    1029            0 :     GNUNET_free (pwh->coin_data);
    1030            0 :     for (size_t i = 0; i < pwh->init_num_coins; i++)
    1031            0 :       TALER_denom_pub_free (&pwh->init_denoms_pub[i].key);
    1032            0 :     GNUNET_free (pwh->init_denoms_pub);
    1033            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    1034              :   }
    1035              :   /* Free init data - no longer needed after prepare_coins */
    1036          156 :   for (size_t i = 0; i < pwh->init_num_coins; i++)
    1037           81 :     TALER_denom_pub_free (&pwh->init_denoms_pub[i].key);
    1038           75 :   GNUNET_free (pwh->init_denoms_pub);
    1039              : 
    1040           75 :   if (0 < pwh->num_bp_coins)
    1041              :   {
    1042              :     /* There are CS denominations; start the blinding-prepare request */
    1043           35 :     pwh->blinding_prepare_handle =
    1044           35 :       TALER_EXCHANGE_post_blinding_prepare_for_withdraw_create (
    1045              :         pwh->curl_ctx,
    1046              :         pwh->exchange_url,
    1047              :         &pwh->blinding_seed,
    1048              :         pwh->num_bp_nonce_keys,
    1049              :         pwh->bp_nonce_keys);
    1050           35 :     if (NULL == pwh->blinding_prepare_handle)
    1051            0 :       return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    1052              :     {
    1053              :       enum TALER_ErrorCode ec =
    1054           35 :         TALER_EXCHANGE_post_blinding_prepare_start (
    1055              :           pwh->blinding_prepare_handle,
    1056              :           &blinding_prepare_done,
    1057              :           pwh);
    1058           35 :       if (TALER_EC_NONE != ec)
    1059              :       {
    1060            0 :         pwh->blinding_prepare_handle = NULL;
    1061            0 :         return ec;
    1062              :       }
    1063              :     }
    1064           35 :     return TALER_EC_NONE;
    1065              :   }
    1066              : 
    1067              :   /* No CS denominations; proceed directly to the withdraw protocol */
    1068           40 :   return call_withdraw_blinded (pwh);
    1069              : }
    1070              : 
    1071              : 
    1072              : void
    1073           75 : TALER_EXCHANGE_post_withdraw_cancel (
    1074              :   struct TALER_EXCHANGE_PostWithdrawHandle *wh)
    1075              : {
    1076           75 :   uint8_t kappa = wh->with_age_proof ? TALER_CNC_KAPPA : 1;
    1077              : 
    1078              :   /* Cleanup init data if _start was never called (or failed) */
    1079           75 :   if (NULL != wh->init_denoms_pub)
    1080              :   {
    1081            0 :     for (size_t i = 0; i < wh->init_num_coins; i++)
    1082            0 :       TALER_denom_pub_free (&wh->init_denoms_pub[i].key);
    1083            0 :     GNUNET_free (wh->init_denoms_pub);
    1084              :   }
    1085              :   /* Cleanup coin data */
    1086           75 :   if (NULL != wh->coin_data)
    1087              :   {
    1088          156 :     for (unsigned int i = 0; i < wh->num_coins; i++)
    1089              :     {
    1090           81 :       struct CoinData *cd = &wh->coin_data[i];
    1091              : 
    1092          180 :       for (uint8_t k = 0; k < kappa; k++)
    1093              :       {
    1094           99 :         struct TALER_PlanchetDetail *planchet = &cd->planchet_details[k];
    1095           99 :         struct CoinCandidate *can = &cd->candidates[k];
    1096              : 
    1097           99 :         TALER_blinded_planchet_free (&planchet->blinded_planchet);
    1098           99 :         TALER_denom_ewv_free (&can->details.blinding_values);
    1099           99 :         TALER_age_commitment_proof_free (&can->details.age_commitment_proof);
    1100              :       }
    1101           81 :       TALER_denom_pub_free (&cd->denom_pub.key);
    1102              :     }
    1103              :   }
    1104              : 
    1105           75 :   TALER_EXCHANGE_post_blinding_prepare_cancel (wh->blinding_prepare_handle);
    1106           75 :   wh->blinding_prepare_handle = NULL;
    1107           75 :   TALER_EXCHANGE_post_withdraw_blinded_cancel (wh->withdraw_blinded_handle);
    1108           75 :   wh->withdraw_blinded_handle = NULL;
    1109              : 
    1110           75 :   GNUNET_free (wh->bp_coins);
    1111           75 :   GNUNET_free (wh->bp_nonces);
    1112           75 :   GNUNET_free (wh->bp_nonce_keys);
    1113           75 :   GNUNET_free (wh->coin_data);
    1114           75 :   TALER_EXCHANGE_keys_decref (wh->keys);
    1115           75 :   GNUNET_free (wh);
    1116           75 : }
    1117              : 
    1118              : 
    1119              : /* exchange_api_post-withdraw.c */
        

Generated by: LCOV version 2.0-1