diff --git a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c index e69de29bb..65bbb4326 100644 --- a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c +++ b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.c @@ -0,0 +1,303 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see +*/ +/** + * @file taler-exchange-httpd_age-withdraw_reveal.c + * @brief Handle /age-withdraw/$ACH/reveal requests + * @author Özgür Kesim + */ +#include "platform.h" +#include +#include +#include +#include "taler_mhd_lib.h" +#include "taler-exchange-httpd_mhd.h" +#include "taler-exchange-httpd_age-withdraw_reveal.h" +#include "taler-exchange-httpd_responses.h" +#include "taler-exchange-httpd_keys.h" + +/** + * State for an /age-withdraw/$ACH/reveal operation. + */ +struct AgeRevealContext +{ + + /** + * Commitment for the age-withdraw operation. + */ + struct TALER_AgeWithdrawCommitmentHashP ach; + + /** + * Public key of the reserve for with the age-withdraw commitment was + * originally made. This parameter is provided by the client again + * during the call to reveal in order to save a database-lookup . + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Number of coins/denonations in the reveal + */ + uint32_t num_coins; + + /** + * TODO:oec num_coins denoms + */ + struct TALER_DenominationHashP *denoms_h; + + /** + * TODO:oec num_coins blinded coins + */ + struct TALER_BlindedCoinHashP *coin_evs; + + /** + * TODO:oec num_coins*(kappa - 1) disclosed coins + */ + struct GNUNET_CRYPTO_EddsaPrivateKey *disclosed_coins; + +}; + +/** + * Helper function to free resources in the context + */ +void +age_reveal_context_free (struct AgeRevealContext *actx) +{ + GNUNET_free (actx->denoms_h); + GNUNET_free (actx->coin_evs); + GNUNET_free (actx->disclosed_coins); +} + + +/** + * Handle a "/age-withdraw/$ACH/reveal request. Parses the given JSON + * ... TODO:oec:description + * + * @param connection The MHD connection to handle + * @param actx The context of the operation, only partially built at call time + * @param j_denoms_h Array of hashes of the denominations for the withdrawal, in JSON format + * @param j_coin_evs The blinded envelopes in JSON format for the coins that are not revealed and will be signed on success + * @param j_disclosed_coins The n*(kappa-1) disclosed coins' private keys in JSON format, from which all other attributes (age restriction, blinding, nonce) will be derived from + */ +MHD_RESULT +handle_age_withdraw_reveal_json ( + struct MHD_Connection *connection, + struct AgeRevealContext *actx, + const json_t *j_denoms_h, + const json_t *j_coin_evs, + const json_t *j_disclosed_coins) +{ + MHD_RESULT mhd_ret = MHD_NO; + + /* Verify JSON-structure consistency */ + { + const char *error = NULL; + + actx->num_coins = json_array_size (j_denoms_h); /* 0, if j_denoms_h is not an array */ + + if (! json_is_array (j_denoms_h)) + error = "denoms_h must be an array"; + else if (! json_is_array (j_coin_evs)) + error = "coin_evs must be an array"; + else if (! json_is_array (j_disclosed_coins)) + error = "disclosed_coins must be an array"; + else if (actx->num_coins == 0) + error = "denoms_h must not be empty"; + else if (actx->num_coins != json_array_size (j_coin_evs)) + error = "denoms_h and coins_evs must be arrays of the same size"; + else if (actx->num_coins * (TALER_CNC_KAPPA - 1) + != json_array_size (j_disclosed_coins)) + error = "the size of array disclosed_coins must be " + TALER_CNC_KAPPA_MINUS_ONE_STR " times of the size of denoms_h"; + else if (actx->num_coins > TALER_MAX_FRESH_COINS) + /** + * FIXME?: If the user had commited to more than the maximum coins allowed, + * the reserve has been charged, but now the user can not withdraw any money + * from it. How can the user get their money back? + **/ + error = "maximum number of coins that can be withdrawn has been exceeded"; + + if (NULL != error) + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + error); + } + + /* Parse denomination keys */ + { + unsigned int idx; + json_t *jh; + + actx->denoms_h = GNUNET_new_array (actx->num_coins, + struct TALER_DenominationHashP); + + json_array_foreach (j_denoms_h, idx, jh) { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto (NULL, &actx->denoms_h[idx]), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (jh, spec, NULL, NULL)) + { + char msg[256] = {0}; + GNUNET_snprintf (msg, + sizeof(msg), + "couldn't parse entry no. %d in array denoms_h", + idx + 1); + mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + msg); + goto EXIT; + } + + }; + } + + /* Parse blinded envelopes */ + { + unsigned int idx; + json_t *ce; + + actx->coin_evs = GNUNET_new_array (actx->num_coins, + struct TALER_BlindedCoinHashP); + + json_array_foreach (j_coin_evs, idx, ce) { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto (NULL, &actx->coin_evs[idx]), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (ce, spec, NULL, NULL)) + { + char msg[256] = {0}; + GNUNET_snprintf (msg, + sizeof(msg), + "couldn't parse entry no. %d in array coin_evs", + idx + 1); + mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + msg); + goto EXIT; + } + }; + } + + /* Parse diclosed keys */ + { + unsigned int idx; + json_t *dc; + + actx->disclosed_coins = GNUNET_new_array ( + actx->num_coins * (TALER_CNC_KAPPA), + struct GNUNET_CRYPTO_EddsaPrivateKey); + + json_array_foreach (j_coin_evs, idx, dc) { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto (NULL, &actx->disclosed_coins[idx]), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (dc, spec, NULL, NULL)) + { + char msg[256] = {0}; + GNUNET_snprintf (msg, + sizeof(msg), + "couldn't parse entry no. %d in array disclosed_coins", + idx + 1); + mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + msg); + goto EXIT; + } + }; + + } + + /* TODO:oec: find commitment */ + /* TODO:oec: check validity of denoms */ + /* TODO:oec: check amount total against denoms */ + /* TODO:oec: compute the disclosed blinded coins */ + /* TODO:oec: generate h_commitment_comp */ + /* TODO:oec: compare h_commitment_comp against h_commitment */ + /* TODO:oec: sign the coins */ + /* TODO:oec: send response */ + + + /* TODO */ +EXIT: + age_reveal_context_free (actx); + return mhd_ret; +} + + +MHD_RESULT +TEH_handler_age_withdraw_reveal ( + struct TEH_RequestContext *rc, + const struct TALER_AgeWithdrawCommitmentHashP *ach, + const json_t *root) +{ + struct AgeRevealContext actx = {0}; + json_t *j_denoms_h; + json_t *j_coin_evs; + json_t *j_disclosed_coins; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("reserve_pub", &actx.reserve_pub), + GNUNET_JSON_spec_json ("denoms_h", &j_denoms_h), + GNUNET_JSON_spec_json ("coin_evs", &j_coin_evs), + GNUNET_JSON_spec_json ("disclosed_coins", &j_disclosed_coins), + GNUNET_JSON_spec_end () + }; + + actx.ach = *ach; + + /* Parse JSON body*/ + { + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (rc->connection, + root, + spec); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; + } + } + + + /* handle reveal request */ + { + MHD_RESULT res; + + res = handle_age_withdraw_reveal_json (rc->connection, + &actx, + j_denoms_h, + j_coin_evs, + j_disclosed_coins); + + GNUNET_JSON_parse_free (spec); + return res; + } + +} + + +/* end of taler-exchange-httpd_age-withdraw_reveal.c */ diff --git a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.h b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.h index ed7e37b3b..73ebc6fb5 100644 --- a/src/exchange/taler-exchange-httpd_age-withdraw_reveal.h +++ b/src/exchange/taler-exchange-httpd_age-withdraw_reveal.h @@ -50,7 +50,7 @@ MHD_RESULT TEH_handler_age_withdraw_reveal ( struct TEH_RequestContext *rc, - const struct TALER_AgeRestrictedCoinsCommitmentP *ach, + const struct TALER_AgeWithdrawCommitmentHashP *ach, const json_t *root); #endif