diff --git a/src/include/taler_mint_service.h b/src/include/taler_mint_service.h index c9dbfb874..b228acc5e 100644 --- a/src/include/taler_mint_service.h +++ b/src/include/taler_mint_service.h @@ -216,7 +216,7 @@ struct TALER_MINT_AuditorInformation * Array of length @a denom_keys with the denomination * keys audited by this auditor. Note that the array * elements point to the same locations as the entries - * in the key's main `denom_keys` array. + * in the key's main `denom_keys` array. */ struct TALER_MINT_DenomPublicKey *const*denom_keys; }; @@ -622,9 +622,269 @@ void TALER_MINT_withdraw_sign_cancel (struct TALER_MINT_WithdrawSignHandle *sign); -/* ********************* /admin/add/incoming *********************** */ +/* ********************* /refresh/melt+reveal ***************************** */ +/** + * Melt (partially spent) coins to obtain fresh coins that are + * unlinkable to the original coin(s). Note that melting more + * than one coin in a single request will make those coins linkable, + * so the safest operation only melts one coin at a time. + * + * This API is typically used by a wallet. Note that to ensure that + * no money is lost in case of hardware failures, is operation does + * not actually initiate the request. Instead, it generates a buffer + * which the caller must store before proceeding with the actual call + * to #TALER_MINT_refresh_execute() that will generate the request. + * + * This function does verify that the given request data is internally + * consistent. However, the @a melts_sigs are only verified if @a + * check_sigs is set to #GNUNET_YES, as this may be relatively + * expensive and should be redundant. + * + * Aside from some non-trivial cryptographic operations that might + * take a bit of CPU time to complete, this function returns + * its result immediately and does not start any asynchronous + * processing. This function is also thread-safe. + * + * @param num_melts number of coins that are being melted (typically 1) + * @param melt_privs array of @a num_melts private keys of the coins to melt + * @param melt_amounts array of @a num_melts amounts specifying how much + * each coin will contribute to the melt (including fee) + * @param melt_sigs array of @a num_melts signatures affirming the + * validity of the public keys corresponding to the + * @a melt_privs private keys + * @param melt_pks array of @a num_melts denomination key information + * records corresponding to the @a melt_sigs + * validity of the keys + * @param check_sigs verify the validity of the signatures of @a melt_sigs + * @param fresh_pks_len length of the @a pks array + * @param fresh_pks array of @a pks_len denominations of fresh coins to create + * @param[OUT] res_size set to the size of the return value, or 0 on error + * @return NULL + * if the inputs are invalid (i.e. denomination key not with this mint). + * Otherwise, pointer to a buffer of @a res_size to store persistently + * before proceeding to #TALER_MINT_refresh_execute(). + * Non-null results should be freed using #GNUNET_free(). + */ +char * +TALER_MINT_refresh_prepare (unsigned int num_melts, + const struct TALER_CoinSpendPrivateKeyP *melt_privs, + const struct TALER_Amount *melt_amounts, + const struct TALER_DenominationSignature *melt_sigs, + const struct TALER_MINT_DenomPublicKey *melt_pks, + int check_sigs, + unsigned int fresh_pks_len, + const struct TALER_MINT_DenomPublicKey *fresh_pks, + size_t *res_size); + + +/* ********************* /refresh/melt ***************************** */ + +/** + * @brief A /refresh/melt Handle + */ +struct TALER_MINT_RefreshMeltHandle; + + +/** + * Callbacks of this type are used to notify the application about the + * result of the /refresh/melt stage. If successful, the @a noreveal_index + * should be committed to disk prior to proceeding #TALER_MINT_refresh_reveal(). + * + * @param cls closure + * @param http_status HTTP response code, never #MHD_HTTP_OK (200) as for successful intermediate response this callback is skipped. + * 0 if the mint's reply is bogus (fails to follow the protocol) + * @param noreveal_index choice by the mint in the cut-and-choose protocol, + * UINT16_MAX on error + * @param full_response full response from the mint (for logging, in case of errors) + */ +typedef void +(*TALER_MINT_RefreshMeltCallback) (void *cls, + unsigned int http_status, + uint16_t noreveal_index, + json_t *full_response); + + +/** + * Submit a melt request to the mint and get the mint's + * response. + * + * This API is typically used by a wallet. Note that to ensure that + * no money is lost in case of hardware failures, the provided + * argument should have been constructed using + * #TALER_MINT_refresh_prepare and committed to persistent storage + * prior to calling this function. + * + * @param mint the mint handle; the mint must be ready to operate + * @param refresh_data_length size of the @a refresh_data (returned + * in the `res_size` argument from #TALER_MINT_refresh_prepare()) + * @param refresh_data the refresh data as returned from + #TALER_MINT_refresh_prepare()) + * @param melt_cb the callback to call with the result + * @param melt_cb_cls closure for @a melt_cb + * @return a handle for this request; NULL if the argument was invalid. + * In this case, neither callback will be called. + */ +struct TALER_MINT_RefreshMeltHandle * +TALER_MINT_refresh_melt_execute (struct TALER_MINT_Handle *mint, + size_t refresh_data_length, + const char *refresh_data, + TALER_MINT_RefreshMeltCallback melt_cb, + void *melt_cb_cls); + + +/** + * Cancel a refresh melt request. This function cannot be used + * on a request handle if the callback was already invoked. + * + * @param rmh the refresh handle + */ +void +TALER_MINT_refresh_melt_cancel (struct TALER_MINT_RefreshMeltHandle *rmh); + + +/* ********************* /refresh/reveal ***************************** */ + + +/** + * Callbacks of this type are used to return the final result of + * submitting a refresh request to a mint. If the operation was + * successful, this function returns the signatures over the coins + * that were remelted. The @a coin_privs and @a sigs arrays give the + * coins in the same order (and should have the same length) in which + * the original request specified the respective denomination keys. + * + * @param cls closure + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request + * 0 if the mint's reply is bogus (fails to follow the protocol) + * @param num_coins number of fresh coins created, length of the @a sigs and @a coin_privs arrays, 0 if the operation failed + * @param coin_privs array of @a num_coins private keys for the coins that were created, NULL on error + * @param sigs array of signature over @a num_coins coins, NULL on error + * @param full_response full response from the mint (for logging, in case of errors) + */ +typedef void +(*TALER_MINT_RefreshRevealCallback) (void *cls, + unsigned int http_status, + + unsigned int num_coins, + const struct TALER_CoinSpendPrivateKeyP *coin_privs, + const struct TALER_DenominationSignature *sigs, + json_t *full_response); + + +/** + * @brief A /refresh/reveal Handle + */ +struct TALER_MINT_RefreshRevealHandle; + + +/** + * Submit a /refresh/reval request to the mint and get the mint's + * response. + * + * This API is typically used by a wallet. Note that to ensure that + * no money is lost in case of hardware failures, the provided + * arguments should have been committed to persistent storage + * prior to calling this function. + * + * @param mint the mint handle; the mint must be ready to operate + * @param refresh_data_length size of the @a refresh_data (returned + * in the `res_size` argument from #TALER_MINT_refresh_prepare()) + * @param refresh_data the refresh data as returned from + #TALER_MINT_refresh_prepare()) + * @param noreveal_index response from the mint to the + * #TALER_MINT_refresh_melt() invocation + * @param reveal_cb the callback to call with the final result of the + * refresh operation + * @param reveal_cb_cls closure for the above callback + * @return a handle for this request; NULL if the argument was invalid. + * In this case, neither callback will be called. + */ +struct TALER_MINT_RefreshRevealHandle * +TALER_MINT_refresh_reveal (struct TALER_MINT_Handle *mint, + size_t refresh_data_length, + const char *refresh_data, + uint16_t noreveal_index, + TALER_MINT_RefreshRevealCallback reveal_cb, + void *reveal_cb_cls); + + +/** + * Cancel a refresh reveal request. This function cannot be used + * on a request handle if the callback was already invoked. + * + * @param rrh the refresh reval handle + */ +void +TALER_MINT_refresh_reveal_cancel (struct TALER_MINT_RefreshRevealHandle *rrh); + + +/* ********************* /refresh/link ***************************** */ + + +/** + * @brief A /refresh/link Handle + */ +struct TALER_MINT_RefreshLinkHandle; + + +/** + * Callbacks of this type are used to return the final result of + * submitting a /refresh/link request to a mint. If the operation was + * successful, this function returns the signatures over the coins + * that were created when the original coin was melted. + * + * @param cls closure + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request + * 0 if the mint's reply is bogus (fails to follow the protocol) + * @param num_coins number of fresh coins created, length of the @a sigs and @a coin_privs arrays, 0 if the operation failed + * @param coin_privs array of @a num_coins private keys for the coins that were created, NULL on error + * @param sigs array of signature over @a num_coins coins, NULL on error + * @param full_response full response from the mint (for logging, in case of errors) + */ +typedef void +(*TALER_MINT_RefreshLinkCallback) (void *cls, + unsigned int http_status, + unsigned int num_coins, + const struct TALER_CoinSpendPrivateKeyP *coin_privs, + const struct TALER_DenominationSignature *sigs, + json_t *full_response); + + +/** + * Submit a link request to the mint and get the mint's response. + * + * This API is typically not used by anyone, it is more a threat + * against those trying to receive a funds transfer by abusing the + * /refresh protocol. + * + * @param mint the mint handle; the mint must be ready to operate + * @param coin_priv private key to request link data for + * @param link_cb the callback to call with the useful result of the + * refresh operation the @a coin_priv was involved in (if any) + * @param link_cb_cls closure for @a link_cb + * @return a handle for this request + */ +struct TALER_MINT_RefreshLinkHandle * +TALER_MINT_refresh_link (struct TALER_MINT_Handle *mint, + const struct TALER_CoinSpendPrivateKeyP *coin_priv, + TALER_MINT_RefreshLinkCallback link_cb, + void *link_cb_cls); + + +/** + * Cancel a refresh link request. This function cannot be used + * on a request handle if the callback was already invoked. + * + * @param rlh the refresh link handle + */ +void +TALER_MINT_refresh_link_cancel (struct TALER_MINT_RefreshLinkHandle *rlh); + + +/* ********************* /admin/add/incoming *********************** */ + /** * @brief A /admin/add/incoming Handle diff --git a/src/mint-lib/Makefile.am b/src/mint-lib/Makefile.am index e3a26be03..400fc77e0 100644 --- a/src/mint-lib/Makefile.am +++ b/src/mint-lib/Makefile.am @@ -19,6 +19,8 @@ libtalermint_la_SOURCES = \ mint_api_handle.c mint_api_handle.h \ mint_api_admin.c \ mint_api_deposit.c \ + mint_api_refresh.c \ + mint_api_refresh_link.c \ mint_api_withdraw.c libtalermint_la_LIBADD = \ diff --git a/src/mint-lib/mint_api_refresh.c b/src/mint-lib/mint_api_refresh.c new file mode 100644 index 000000000..723fe7070 --- /dev/null +++ b/src/mint-lib/mint_api_refresh.c @@ -0,0 +1,540 @@ +/* + This file is part of TALER + Copyright (C) 2015 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file mint-lib/mint_api_refresh.c + * @brief Implementation of the /refresh/melt+reveal requests of the mint's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include "taler_mint_service.h" +#include "mint_api_json.h" +#include "mint_api_context.h" +#include "mint_api_handle.h" +#include "taler_signatures.h" + + +/* ********************* /refresh/ common ***************************** */ + + +/** + * Melt (partially spent) coins to obtain fresh coins that are + * unlinkable to the original coin(s). Note that melting more + * than one coin in a single request will make those coins linkable, + * so the safest operation only melts one coin at a time. + * + * This API is typically used by a wallet. Note that to ensure that + * no money is lost in case of hardware failures, is operation does + * not actually initiate the request. Instead, it generates a buffer + * which the caller must store before proceeding with the actual call + * to #TALER_MINT_refresh_execute() that will generate the request. + * + * This function does verify that the given request data is internally + * consistent. However, the @a melts_sigs are only verified if @a + * check_sigs is set to #GNUNET_YES, as this may be relatively + * expensive and should be redundant. + * + * Aside from some non-trivial cryptographic operations that might + * take a bit of CPU time to complete, this function returns + * its result immediately and does not start any asynchronous + * processing. This function is also thread-safe. + * + * @param num_melts number of coins that are being melted (typically 1) + * @param melt_privs array of @a num_melts private keys of the coins to melt + * @param melt_amounts array of @a num_melts amounts specifying how much + * each coin will contribute to the melt (including fee) + * @param melt_sigs array of @a num_melts signatures affirming the + * validity of the public keys corresponding to the + * @a melt_privs private keys + * @param melt_pks array of @a num_melts denomination key information + * records corresponding to the @a melt_sigs + * validity of the keys + * @param check_sigs verify the validity of the signatures of @a melt_sigs + * @param fresh_pks_len length of the @a pks array + * @param fresh_pks array of @a pks_len denominations of fresh coins to create + * @param[OUT] res_size set to the size of the return value, or 0 on error + * @return NULL + * if the inputs are invalid (i.e. denomination key not with this mint). + * Otherwise, pointer to a buffer of @a res_size to store persistently + * before proceeding to #TALER_MINT_refresh_execute(). + * Non-null results should be freed using #GNUNET_free(). + */ +char * +TALER_MINT_refresh_prepare (unsigned int num_melts, + const struct TALER_CoinSpendPrivateKeyP *melt_privs, + const struct TALER_Amount *melt_amounts, + const struct TALER_DenominationSignature *melt_sigs, + const struct TALER_MINT_DenomPublicKey *melt_pks, + int check_sigs, + unsigned int fresh_pks_len, + const struct TALER_MINT_DenomPublicKey *fresh_pks, + size_t *res_size) +{ + GNUNET_break (0); // FIXME: not implemented + *res_size = 0; + return NULL; +} + + +/* ********************* /refresh/melt ***************************** */ + + +/** + * @brief A /refresh/melt Handle + */ +struct TALER_MINT_RefreshMeltHandle +{ + + /** + * The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this request. + */ + char *url; + + /** + * JSON encoding of the request to POST. + */ + char *json_enc; + + /** + * Handle for the request. + */ + struct MAC_Job *job; + + /** + * Function to call with refresh melt failure results. + */ + TALER_MINT_RefreshMeltCallback melt_cb; + + /** + * Closure for @e result_cb and @e melt_failure_cb. + */ + void *melt_cb_cls; + + /** + * Download buffer + */ + struct MAC_DownloadBuffer db; + +}; + + +/** + * Function called when we're done processing the + * HTTP /refresh/melt request. + * + * @param cls the `struct TALER_MINT_RefreshMeltHandle` + * @param eh the curl request handle + */ +static void +handle_refresh_melt_finished (void *cls, + CURL *eh) +{ + struct TALER_MINT_RefreshMeltHandle *rmh = cls; + long response_code; + json_t *json; + + rmh->job = NULL; + json = MAC_download_get_result (&rmh->db, + eh, + &response_code); + switch (response_code) + { + case 0: + break; + case MHD_HTTP_OK: + GNUNET_break (0); // FIXME: NOT implemented! (parse, check sig!) + + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the mint is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_FORBIDDEN: + /* Double spending; check signatures on transaction history */ + GNUNET_break (0); // FIXME: NOT implemented! + break; + case MHD_HTTP_UNAUTHORIZED: + /* Nothing really to verify, mint says one of the signatures is + invalid; assuming we checked them, this should never happen, we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + response_code); + GNUNET_break (0); + response_code = 0; + break; + } + if (NULL != rmh->melt_cb) + rmh->melt_cb (rmh->melt_cb_cls, + response_code, + UINT16_MAX, + json); + json_decref (json); + TALER_MINT_refresh_melt_cancel (rmh); +} + + +/** + * Submit a melt request to the mint and get the mint's + * response. + * + * This API is typically used by a wallet. Note that to ensure that + * no money is lost in case of hardware failures, the provided + * argument should have been constructed using + * #TALER_MINT_refresh_prepare and committed to persistent storage + * prior to calling this function. + * + * @param mint the mint handle; the mint must be ready to operate + * @param refresh_data_length size of the @a refresh_data (returned + * in the `res_size` argument from #TALER_MINT_refresh_prepare()) + * @param refresh_data the refresh data as returned from + #TALER_MINT_refresh_prepare()) + * @param melt_cb the callback to call with the result + * @param melt_cb_cls closure for @a melt_cb + * @return a handle for this request; NULL if the argument was invalid. + * In this case, neither callback will be called. + */ +struct TALER_MINT_RefreshMeltHandle * +TALER_MINT_refresh_melt (struct TALER_MINT_Handle *mint, + size_t refresh_data_length, + const char *refresh_data, + TALER_MINT_RefreshMeltCallback melt_cb, + void *melt_cb_cls) +{ + json_t *melt_obj; + struct TALER_MINT_RefreshMeltHandle *rmh; + CURL *eh; + struct TALER_MINT_Context *ctx; + + if (GNUNET_YES != + MAH_handle_is_ready (mint)) + { + GNUNET_break (0); + return NULL; + } + /* FIXME: parse "refresh_data" */ + + /* FIXME: totally bogus request building here: */ + melt_obj = json_pack ("{s:o, s:O}", /* f/wire */ + "4", 42, + "6", 62); + + + rmh = GNUNET_new (struct TALER_MINT_RefreshMeltHandle); + rmh->mint = mint; + rmh->melt_cb = melt_cb; + rmh->melt_cb_cls = melt_cb_cls; + + rmh->url = MAH_path_to_url (mint, + "/refresh/melt"); + + eh = curl_easy_init (); + GNUNET_assert (NULL != (rmh->json_enc = + json_dumps (melt_obj, + JSON_COMPACT))); + json_decref (melt_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + rmh->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDS, + rmh->json_enc)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDSIZE, + strlen (rmh->json_enc))); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEFUNCTION, + &MAC_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEDATA, + &rmh->db)); + ctx = MAH_handle_to_context (mint); + rmh->job = MAC_job_add (ctx, + eh, + GNUNET_YES, + &handle_refresh_melt_finished, + rmh); + return rmh; +} + + +/** + * Cancel a refresh execute request. This function cannot be used + * on a request handle if either callback was already invoked. + * + * @param rmh the refresh melt handle + */ +void +TALER_MINT_refresh_melt_cancel (struct TALER_MINT_RefreshMeltHandle *rmh) +{ + if (NULL != rmh->job) + { + MAC_job_cancel (rmh->job); + rmh->job = NULL; + } + GNUNET_free_non_null (rmh->db.buf); + GNUNET_free (rmh->url); + GNUNET_free (rmh->json_enc); + GNUNET_free (rmh); +} + + +/* ********************* /refresh/reveal ***************************** */ + + +/** + * @brief A /refresh/reveal Handle + */ +struct TALER_MINT_RefreshRevealHandle +{ + + /** + * The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this request. + */ + char *url; + + /** + * JSON encoding of the request to POST. + */ + char *json_enc; + + /** + * Handle for the request. + */ + struct MAC_Job *job; + + /** + * Function to call with the result. + */ + TALER_MINT_RefreshRevealCallback reveal_cb; + + /** + * Closure for @e reveal_cb. + */ + void *reveal_cb_cls; + + /** + * Download buffer + */ + struct MAC_DownloadBuffer db; + + +}; + + +/** + * Function called when we're done processing the + * HTTP /refresh/reveal request. + * + * @param cls the `struct TALER_MINT_RefreshHandle` + * @param eh the curl request handle + */ +static void +handle_refresh_reveal_finished (void *cls, + CURL *eh) +{ + struct TALER_MINT_RefreshRevealHandle *rrh = cls; + long response_code; + json_t *json; + + rrh->job = NULL; + json = MAC_download_get_result (&rrh->db, + eh, + &response_code); + switch (response_code) + { + case 0: + break; + case MHD_HTTP_OK: + GNUNET_break (0); // FIXME: NOT implemented! + // rrh->reveal_cb = NULL; (call with real result, do not call again below) + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the mint is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_CONFLICT: + /* Nothing really to verify, mint says our reveal is inconsitent + with our commitment, so either side is buggy; we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + response_code); + GNUNET_break (0); + response_code = 0; + break; + } + if (NULL != rrh->reveal_cb) + rrh->reveal_cb (rrh->reveal_cb_cls, + response_code, + 0, NULL, NULL, + json); + json_decref (json); + TALER_MINT_refresh_reveal_cancel (rrh); +} + + + +/** + * Submit a /refresh/reval request to the mint and get the mint's + * response. + * + * This API is typically used by a wallet. Note that to ensure that + * no money is lost in case of hardware failures, the provided + * arguments should have been committed to persistent storage + * prior to calling this function. + * + * @param mint the mint handle; the mint must be ready to operate + * @param refresh_data_length size of the @a refresh_data (returned + * in the `res_size` argument from #TALER_MINT_refresh_prepare()) + * @param refresh_data the refresh data as returned from + #TALER_MINT_refresh_prepare()) + * @param noreveal_index response from the mint to the + * #TALER_MINT_refresh_melt() invocation + * @param reveal_cb the callback to call with the final result of the + * refresh operation + * @param reveal_cb_cls closure for the above callback + * @return a handle for this request; NULL if the argument was invalid. + * In this case, neither callback will be called. + */ +struct TALER_MINT_RefreshRevealHandle * +TALER_MINT_refresh_reveal (struct TALER_MINT_Handle *mint, + size_t refresh_data_length, + const char *refresh_data, + uint16_t noreveal_index, + TALER_MINT_RefreshRevealCallback reveal_cb, + void *reveal_cb_cls) +{ + struct TALER_MINT_RefreshRevealHandle *rrh; + json_t *reveal_obj; + CURL *eh; + struct TALER_MINT_Context *ctx; + + if (GNUNET_YES != + MAH_handle_is_ready (mint)) + { + GNUNET_break (0); + return NULL; + } + /* FIXME: parse "refresh_data" */ + + /* FIXME: totally bogus request building here: */ + reveal_obj = json_pack ("{s:o, s:O}", /* f/wire */ + "4", 42, + "6", 62); + + rrh = GNUNET_new (struct TALER_MINT_RefreshRevealHandle); + rrh->mint = mint; + rrh->reveal_cb = reveal_cb; + rrh->reveal_cb_cls = reveal_cb_cls; + + rrh->url = MAH_path_to_url (rrh->mint, + "/refresh/reveal"); + + eh = curl_easy_init (); + GNUNET_assert (NULL != (rrh->json_enc = + json_dumps (reveal_obj, + JSON_COMPACT))); + json_decref (reveal_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + rrh->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDS, + rrh->json_enc)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDSIZE, + strlen (rrh->json_enc))); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEFUNCTION, + &MAC_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEDATA, + &rrh->db)); + ctx = MAH_handle_to_context (rrh->mint); + rrh->job = MAC_job_add (ctx, + eh, + GNUNET_YES, + &handle_refresh_reveal_finished, + rrh); + return rrh; +} + + +/** + * Cancel a refresh reveal request. This function cannot be used + * on a request handle if the callback was already invoked. + * + * @param rrh the refresh reval handle + */ +void +TALER_MINT_refresh_reveal_cancel (struct TALER_MINT_RefreshRevealHandle *rrh) +{ + if (NULL != rrh->job) + { + MAC_job_cancel (rrh->job); + rrh->job = NULL; + } + GNUNET_free_non_null (rrh->db.buf); + GNUNET_free (rrh->url); + GNUNET_free (rrh->json_enc); + GNUNET_free (rrh); +} + + +/* end of mint_api_refresh.c */ diff --git a/src/mint-lib/mint_api_refresh_link.c b/src/mint-lib/mint_api_refresh_link.c new file mode 100644 index 000000000..3ea6b23e4 --- /dev/null +++ b/src/mint-lib/mint_api_refresh_link.c @@ -0,0 +1,236 @@ +/* + This file is part of TALER + Copyright (C) 2015 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file mint-lib/mint_api_refresh_link.c + * @brief Implementation of the /refresh/link request of the mint's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include "taler_mint_service.h" +#include "mint_api_json.h" +#include "mint_api_context.h" +#include "mint_api_handle.h" +#include "taler_signatures.h" + + +/** + * @brief A /refresh/link Handle + */ +struct TALER_MINT_RefreshLinkHandle +{ + + /** + * The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this request. + */ + char *url; + + /** + * JSON encoding of the request to POST. + */ + char *json_enc; + + /** + * Handle for the request. + */ + struct MAC_Job *job; + + /** + * Function to call with the result. + */ + TALER_MINT_RefreshLinkCallback link_cb; + + /** + * Closure for @e cb. + */ + void *link_cb_cls; + + /** + * Download buffer + */ + struct MAC_DownloadBuffer db; + +}; + + +/** + * Function called when we're done processing the + * HTTP /refresh/link request. + * + * @param cls the `struct TALER_MINT_RefreshLinkHandle` + * @param eh the curl request handle + */ +static void +handle_refresh_link_finished (void *cls, + CURL *eh) +{ + struct TALER_MINT_RefreshLinkHandle *rlh = cls; + long response_code; + json_t *json; + + rlh->job = NULL; + json = MAC_download_get_result (&rlh->db, + eh, + &response_code); + switch (response_code) + { + case 0: + break; + case MHD_HTTP_OK: + GNUNET_break (0); // FIXME: NOT implemented! + // rh->link_cb = NULL; (call with real result, do not call again below) + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the mint is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, mint says this coin was not melted; we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + response_code); + GNUNET_break (0); + response_code = 0; + break; + } + if (NULL != rlh->link_cb) + rlh->link_cb (rlh->link_cb_cls, + response_code, + 0, NULL, NULL, + json); + json_decref (json); + TALER_MINT_refresh_link_cancel (rlh); +} + + +/** + * Submit a link request to the mint and get the mint's response. + * + * This API is typically not used by anyone, it is more a threat + * against those trying to receive a funds transfer by abusing the + * /refresh protocol. + * + * @param mint the mint handle; the mint must be ready to operate + * @param coin_priv private key to request link data for + * @param link_cb the callback to call with the useful result of the + * refresh operation the @a coin_priv was involved in (if any) + * @param link_cb_cls closure for @a link_cb + * @return a handle for this request + */ +struct TALER_MINT_RefreshLinkHandle * +TALER_MINT_refresh_link (struct TALER_MINT_Handle *mint, + const struct TALER_CoinSpendPrivateKeyP *coin_priv, + TALER_MINT_RefreshLinkCallback link_cb, + void *link_cb_cls) +{ + json_t *link_obj; + struct TALER_MINT_RefreshLinkHandle *rlh; + CURL *eh; + struct TALER_MINT_Context *ctx; + + if (GNUNET_YES != + MAH_handle_is_ready (mint)) + { + GNUNET_break (0); + return NULL; + } + /* FIXME: totally bogus request building here: */ + link_obj = json_pack ("{s:o, s:O}", /* f/wire */ + "4", 42, + "6", 62); + + + rlh = GNUNET_new (struct TALER_MINT_RefreshLinkHandle); + rlh->mint = mint; + rlh->link_cb = link_cb; + rlh->link_cb_cls = link_cb_cls; + + rlh->url = MAH_path_to_url (mint, "/refresh/link"); + + eh = curl_easy_init (); + GNUNET_assert (NULL != (rlh->json_enc = + json_dumps (link_obj, + JSON_COMPACT))); + json_decref (link_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + rlh->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDS, + rlh->json_enc)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDSIZE, + strlen (rlh->json_enc))); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEFUNCTION, + &MAC_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEDATA, + &rlh->db)); + ctx = MAH_handle_to_context (mint); + rlh->job = MAC_job_add (ctx, + eh, + GNUNET_YES, + &handle_refresh_link_finished, + rlh); + return rlh; +} + + +/** + * Cancel a refresh link request. This function cannot be used + * on a request handle if the callback was already invoked. + * + * @param rlh the refresh link handle + */ +void +TALER_MINT_refresh_link_cancel (struct TALER_MINT_RefreshLinkHandle *rlh) +{ + if (NULL != rlh->job) + { + MAC_job_cancel (rlh->job); + rlh->job = NULL; + } + GNUNET_free_non_null (rlh->db.buf); + GNUNET_free (rlh->url); + GNUNET_free (rlh->json_enc); + GNUNET_free (rlh); +} + + +/* end of mint_api_refresh_link.c */ diff --git a/src/mintdb/perf_taler_mintdb_init.c b/src/mintdb/perf_taler_mintdb_init.c index 0e58383bb..725b3be07 100644 --- a/src/mintdb/perf_taler_mintdb_init.c +++ b/src/mintdb/perf_taler_mintdb_init.c @@ -109,7 +109,7 @@ PERF_TALER_MINTDB_denomination_copy (const struct TALER_MINTDB_DenominationKeyIs GNUNET_CRYPTO_rsa_private_key_dup ( dki->denom_priv.rsa_private_key); } {/* denom_pub */ - copy->denom_pub.rsa_public_key = + copy->denom_pub.rsa_public_key = GNUNET_CRYPTO_rsa_public_key_dup (dki->denom_pub.rsa_public_key); } {/* issue */ @@ -174,11 +174,11 @@ struct PERF_TALER_MINTDB_Reserve * PERF_TALER_MINTDB_reserve_copy (const struct PERF_TALER_MINTDB_Reserve *reserve) { struct PERF_TALER_MINTDB_Reserve *copy; - GNUNET_assert (NULL != + GNUNET_assert (NULL != (copy = GNUNET_new (struct PERF_TALER_MINTDB_Reserve))); *copy = *reserve; return copy; -} +} /** @@ -214,8 +214,8 @@ PERF_TALER_MINTDB_deposit_init (const struct PERF_TALER_MINTDB_Coin *coin) "\"BIC\":\"GENODEF1SRL\"" "}"; static uint64_t transaction_id = 0; - struct GNUNET_TIME_Absolute timestamp; - struct GNUNET_TIME_Absolute refund_deadline; + struct GNUNET_TIME_Absolute timestamp; + struct GNUNET_TIME_Absolute refund_deadline; struct TALER_Amount amount_with_fee; struct TALER_Amount deposit_fee; @@ -255,10 +255,10 @@ PERF_TALER_MINTDB_deposit_init (const struct PERF_TALER_MINTDB_Coin *coin) timestamp = GNUNET_TIME_absolute_get (); refund_deadline = GNUNET_TIME_absolute_get (); GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":1.1", + TALER_string_to_amount (CURRENCY ":1.1", &amount_with_fee)); GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":0.1", + TALER_string_to_amount (CURRENCY ":0.1", &deposit_fee)); { deposit->coin.coin_pub = coin->public_info.coin_pub; @@ -292,10 +292,10 @@ PERF_TALER_MINTDB_deposit_copy (const struct TALER_MINTDB_Deposit *deposit) { struct TALER_MINTDB_Deposit *copy; - GNUNET_assert (NULL != (copy = GNUNET_new (struct TALER_MINTDB_Deposit))); + copy = GNUNET_new (struct TALER_MINTDB_Deposit); *copy = *deposit; - json_incref (deposit->wire); - copy->coin.denom_pub.rsa_public_key = + json_incref (copy->wire); + copy->coin.denom_pub.rsa_public_key = GNUNET_CRYPTO_rsa_public_key_dup (deposit->coin.denom_pub.rsa_public_key); copy->coin.denom_sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup (deposit->coin.denom_sig.rsa_signature); @@ -310,7 +310,7 @@ PERF_TALER_MINTDB_deposit_copy (const struct TALER_MINTDB_Deposit *deposit) int PERF_TALER_MINTDB_deposit_free (struct TALER_MINTDB_Deposit *deposit) { - if ( NULL == deposit) + if (NULL == deposit) return GNUNET_OK; GNUNET_CRYPTO_rsa_public_key_free (deposit->coin.denom_pub.rsa_public_key); GNUNET_CRYPTO_rsa_signature_free (deposit->coin.denom_sig.rsa_signature); @@ -369,6 +369,7 @@ PERF_TALER_MINTDB_coin_init ( coin->blind.reserve_pub = reserve->reserve.pub; GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &coin->blind.h_coin_envelope); + return coin; } @@ -389,19 +390,19 @@ PERF_TALER_MINTDB_coin_copy (const struct PERF_TALER_MINTDB_Coin *coin) copy->priv = coin->priv; /* public_info */ copy->public_info.coin_pub = coin->public_info.coin_pub; - copy->public_info.denom_pub.rsa_public_key = + copy->public_info.denom_pub.rsa_public_key = GNUNET_CRYPTO_rsa_public_key_dup (coin->public_info.denom_pub.rsa_public_key); GNUNET_assert (NULL != copy->public_info.denom_pub.rsa_public_key); - copy->public_info.denom_sig.rsa_signature = + copy->public_info.denom_sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup (coin->public_info.denom_sig.rsa_signature); GNUNET_assert (NULL != coin->public_info.denom_sig.rsa_signature); /* blind */ - copy->blind.sig.rsa_signature = + copy->blind.sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup (coin->blind.sig.rsa_signature); GNUNET_assert (NULL != copy->blind.sig.rsa_signature); copy->blind.denom_pub.rsa_public_key = - GNUNET_CRYPTO_rsa_public_key_dup (coin->blind.denom_pub.rsa_public_key); + GNUNET_CRYPTO_rsa_public_key_dup (coin->blind.denom_pub.rsa_public_key); GNUNET_assert (NULL != copy->blind.denom_pub.rsa_public_key); copy->blind.amount_with_fee = coin->blind.amount_with_fee; copy->blind.withdraw_fee = coin->blind.withdraw_fee; @@ -453,7 +454,7 @@ PERF_TALER_MINTDB_refresh_session_init () * @return #GNUNET_OK if the copy was successful, #GNUNET_SYSERR if it wasn't */ int -PERF_TALER_MINTDB_refresh_session_copy (struct TALER_MINTDB_RefreshSession *session, +PERF_TALER_MINTDB_refresh_session_copy (struct TALER_MINTDB_RefreshSession *session, struct TALER_MINTDB_RefreshSession *copy) { *copy = *session; @@ -477,9 +478,9 @@ PERF_TALER_MINTDB_refresh_session_free (struct TALER_MINTDB_RefreshSession *refr /** * Create a melt operation * - * @param session the refresh session + * @param session the refresh session * @param dki the denomination the melted coin uses - * @return a pointer to a #TALER_MINTDB_RefreshMelt + * @return a pointer to a #TALER_MINTDB_RefreshMelt */ struct TALER_MINTDB_RefreshMelt * PERF_TALER_MINTDB_refresh_melt_init (struct GNUNET_HashCode *session, @@ -488,18 +489,18 @@ PERF_TALER_MINTDB_refresh_melt_init (struct GNUNET_HashCode *session, struct TALER_MINTDB_RefreshMelt *melt; struct TALER_CoinSpendSignatureP coin_sig; struct TALER_Amount amount; - struct TALER_Amount amount_with_fee; + struct TALER_Amount amount_with_fee; { - struct + struct { struct GNUNET_CRYPTO_EccSignaturePurpose purpose; struct GNUNET_HashCode session; } to_sign; - - to_sign.purpose.purpose = GNUNET_SIGNATURE_PURPOSE_TEST; + + to_sign.purpose.purpose = GNUNET_SIGNATURE_PURPOSE_TEST; to_sign.purpose.size = htonl (sizeof (to_sign)); - to_sign.session = *session; + to_sign.session = *session; GNUNET_CRYPTO_eddsa_sign (&coin->priv, &to_sign.purpose, &coin_sig.eddsa_signature); @@ -508,9 +509,9 @@ PERF_TALER_MINTDB_refresh_melt_init (struct GNUNET_HashCode *session, &amount)); GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":0.1", &amount_with_fee)); - melt = GNUNET_new (struct TALER_MINTDB_RefreshMelt); + melt = GNUNET_new (struct TALER_MINTDB_RefreshMelt); melt->coin.coin_pub = coin->public_info.coin_pub; - melt->coin.denom_sig.rsa_signature = + melt->coin.denom_sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup (coin->public_info.denom_sig.rsa_signature); melt->coin.denom_pub.rsa_public_key = GNUNET_CRYPTO_rsa_public_key_dup (coin->public_info.denom_pub.rsa_public_key); @@ -526,7 +527,7 @@ PERF_TALER_MINTDB_refresh_melt_init (struct GNUNET_HashCode *session, /** * Copies the internals of a #TALER_MINTDB_RefreshMelt - * + * * @param melt the refresh melt to copy * @return an copy of @ melt */ @@ -561,7 +562,7 @@ PERF_TALER_MINTDB_refresh_melt_free (struct TALER_MINTDB_RefreshMelt *melt) /** - * Create a #TALER_MINTDB_RefreshCommitCoin + * Create a #TALER_MINTDB_RefreshCommitCoin */ struct TALER_MINTDB_RefreshCommitCoin * PERF_TALER_MINTDB_refresh_commit_coin_init () diff --git a/src/mintdb/perf_taler_mintdb_interpreter.c b/src/mintdb/perf_taler_mintdb_interpreter.c index 2d9f7576b..9882b830d 100644 --- a/src/mintdb/perf_taler_mintdb_interpreter.c +++ b/src/mintdb/perf_taler_mintdb_interpreter.c @@ -64,7 +64,7 @@ data_free (struct PERF_TALER_MINTDB_Data *data) return; GNUNET_free (data->data.time); data->data.time = NULL; - return; + return; case PERF_TALER_MINTDB_DEPOSIT: if (NULL == data->data.deposit) @@ -123,8 +123,8 @@ data_copy (const struct PERF_TALER_MINTDB_Data *data, struct PERF_TALER_MINTDB_D return; case PERF_TALER_MINTDB_COIN: - copy->data.coin = - PERF_TALER_MINTDB_coin_copy (data->data.coin); + copy->data.coin + = PERF_TALER_MINTDB_coin_copy (data->data.coin); return; case PERF_TALER_MINTDB_RESERVE: @@ -934,7 +934,7 @@ interpret (struct PERF_TALER_MINTDB_interpreter_state *state) .details.get_reserve.label_reserve))); data = &state->cmd[reserve_index].exposed; - + GNUNET_assert (GNUNET_OK == (state->plugin->reserve_get (state->plugin->cls, state->session,