donau

Donation authority for GNU Taler (experimental)
Log | Files | Refs | Submodules | README | LICENSE

commit 93ff6fdeb348db00218a8540441afa627ef61b96
parent 96de7d08c4f72925f4deebad76d57cb9734535c4
Author: Casaburi Johannes <johannes.casaburi@students.bfh.ch>
Date:   Tue,  2 Jan 2024 15:29:13 +0100

cleanup

Diffstat:
Msrc/donau/Makefile.am | 4+---
Msrc/donau/donau-httpd.c | 2+-
Msrc/donau/donau-httpd.h | 10++--------
Dsrc/donau/donau-httpd_batch-issue_receipts.c | 930-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_batch-issue_receipts.h | 48------------------------------------------------
Dsrc/donau/donau-httpd_charities_close.c | 448-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_charities_close.h | 41-----------------------------------------
Dsrc/donau/donau-httpd_common_submit_receipts.c | 267-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_common_submit_receipts.h | 130-------------------------------------------------------------------------------
Msrc/donau/donau-httpd_config.c | 5+----
Msrc/donau/donau-httpd_db.c | 55-------------------------------------------------------
Dsrc/donau/donau-httpd_issue_receipts.c | 700-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_issue_receipts.h | 47-----------------------------------------------
Msrc/donau/donau-httpd_keys.c | 1478+------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_responses.c | 560-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_responses.h | 201-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_submit_receipts_get.c | 519-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_submit_receipts_get.h | 50--------------------------------------------------
Dsrc/donau/donau-httpd_terms.c | 81-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_terms.h | 65-----------------------------------------------------------------
Msrc/donaudb/pg_add_donation_unit_key.h | 3+--
Dsrc/lib/donau_api_batch_issue_receipts.c | 456-------------------------------------------------------------------------------
Dsrc/lib/donau_api_batch_issue_receipts2.c | 555-------------------------------------------------------------------------------
Dsrc/lib/donau_api_batch_submit_receipts.c | 779-------------------------------------------------------------------------------
Msrc/lib/donau_api_handle.c | 51---------------------------------------------------
Msrc/lib/donau_api_issue_receipts.c | 99-------------------------------------------------------------------------------
Msrc/testing/testing_api_misc.c | 2--
27 files changed, 7 insertions(+), 7579 deletions(-)

diff --git a/src/donau/Makefile.am b/src/donau/Makefile.am @@ -20,9 +20,7 @@ bin_PROGRAMS = \ donau-httpd donau_httpd_SOURCES = \ - donau-httpd.c donau-httpd.h \ - donau-httpd_responses.c donau-httpd_responses.h \ - donau-httpd_terms.c donau-httpd_terms.h + donau-httpd.c donau-httpd.h donau_httpd_LDADD = \ $(LIBGCRYPT_LIBS) \ diff --git a/src/donau/donau-httpd.c b/src/donau/donau-httpd.c @@ -29,11 +29,11 @@ #include <limits.h> #include "taler/taler_templating_lib.h" #include "taler/taler_mhd_lib.h" -#include "donau-httpd_terms.h" #include "donaudb_lib.h" #include "donaudb_plugin.h" #include <gnunet/gnunet_mhd_compat.h> #include "donau_util.h" +#include "donau-httpd.h" /** * Backlog for listen operation on unix domain sockets. diff --git a/src/donau/donau-httpd.h b/src/donau/donau-httpd.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2022 Taler Systems SA + Copyright (C) 2024 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 @@ -14,7 +14,7 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ /** - * @file taler-donau-httpd.h + * @file donau-httpd.h * @brief Global declarations for the donau * @author Florian Dold * @author Benedikt Mueller @@ -70,12 +70,6 @@ extern int DH_check_invariants_flag; extern bool DH_suicide; /** - * Master public key (according to the - * configuration in the donau directory). - */ -extern struct TALER_MasterPublicKeyP DH_master_public_key; - -/** * Key used to encrypt KYC attribute data in our database. */ extern struct TALER_AttributeEncryptionKeyP DH_attribute_key; diff --git a/src/donau/donau-httpd_batch-issue_receipts.c b/src/donau/donau-httpd_batch-issue_receipts.c @@ -1,930 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-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 CHARITYABILITY 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_batch-issue_receipts.c - * @brief Handle /reserves/$RESERVE_PUB/batch-withdraw requests - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include "taler-donau-httpd.h" -#include "taler/taler_json_lib.h" -#include "taler_kyclogic_lib.h" -#include "taler/taler_mhd_lib.h" -#include "taler-donau-httpd_batch-withdraw.h" -#include "taler-donau-httpd_responses.h" -#include "taler-donau-httpd_keys.h" -#include <taler/taler_util.h> - - -/** - * Information per planchet in the batch. - */ -struct PlanchetContext -{ - - /** - * Hash of the (blinded) message to be signed by the Donau. - */ - struct TALER_BlindedCoinHashP h_coin_envelope; - - /** - * Value of the coin being donaud (matching the denomination key) - * plus the transaction fee. We include this in what is being - * signed so that we can verify a reserve's remaining total balance - * without needing to access the respective denomination key - * information each time. - */ - struct TALER_Amount amount_with_fee; - - /** - * Blinded planchet. - */ - struct TALER_BlindedPlanchet blinded_planchet; - - /** - * Set to the resulting signed coin data to be returned to the client. - */ - struct DONAUDB_CollectableBlindcoin collectable; - -}; - -/** - * Context for #batch_withdraw_transaction. - */ -struct BatchWithdrawContext -{ - - /** - * Public key of the reserv. - */ - const struct TALER_ReservePublicKeyP *reserve_pub; - - /** - * request context - */ - const struct DH_RequestContext *rc; - - /** - * KYC status of the reserve used for the operation. - */ - struct DONAUDB_KycStatus kyc; - - /** - * Array of @e planchets_length planchets we are processing. - */ - struct PlanchetContext *planchets; - - /** - * Hash of the payto-URI representing the reserve - * from which we are withdrawing. - */ - struct TALER_PaytoHashP h_payto; - - /** - * Current time for the DB transaction. - */ - struct GNUNET_TIME_Timestamp now; - - /** - * Total amount from all coins with fees. - */ - struct TALER_Amount batch_total; - - /** - * Length of the @e planchets array. - */ - unsigned int planchets_length; - - /** - * AML decision, #TALER_AML_NORMAL if we may proceed. - */ - enum TALER_AmlDecisionState aml_decision; - -}; - - -/** - * Function called to iterate over KYC-relevant - * transaction amounts for a particular time range. - * Called within a database transaction, so must - * not start a new one. - * - * @param cls closure, identifies the event type and - * account to iterate over events for - * @param limit maximum time-range for which events - * should be fetched (timestamp in the past) - * @param cb function to call on each event found, - * events must be returned in reverse chronological - * order - * @param cb_cls closure for @a cb - */ -static void -batch_withdraw_amount_cb (void *cls, - struct GNUNET_TIME_Absolute limit, - DONAUDB_KycAmountCallback cb, - void *cb_cls) -{ - struct BatchWithdrawContext *wc = cls; - enum GNUNET_DB_QueryStatus qs; - - if (GNUNET_OK != - cb (cb_cls, - &wc->batch_total, - wc->now.abs_time)) - return; - qs = DH_plugin->select_withdraw_amounts_for_kyc_check ( - DH_plugin->cls, - &wc->h_payto, - limit, - cb, - cb_cls); - GNUNET_break (qs >= 0); -} - - -/** - * Function called on each @a amount that was found to - * be relevant for the AML check as it was merged into - * the reserve. - * - * @param cls `struct TALER_Amount *` to total up the amounts - * @param amount encountered transaction amount - * @param date when was the amount encountered - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to abort iteration - * #GNUNET_SYSERR on internal error (also abort itaration) - */ -static enum GNUNET_GenericReturnValue -aml_amount_cb ( - void *cls, - const struct TALER_Amount *amount, - struct GNUNET_TIME_Absolute date) -{ - struct TALER_Amount *total = cls; - - GNUNET_assert (0 <= - TALER_amount_add (total, - total, - amount)); - return GNUNET_OK; -} - - -/** - * Generates our final (successful) response. - * - * @param rc request context - * @param wc operation context - * @return MHD queue status - */ -static MHD_RESULT -generate_reply_success (const struct DH_RequestContext *rc, - const struct BatchWithdrawContext *wc) -{ - json_t *sigs; - - if (! wc->kyc.ok) - { - /* KYC required */ - return DH_RESPONSE_reply_kyc_required (rc->connection, - &wc->h_payto, - &wc->kyc); - } - if (TALER_AML_NORMAL != wc->aml_decision) - return DH_RESPONSE_reply_aml_blocked (rc->connection, - wc->aml_decision); - - sigs = json_array (); - GNUNET_assert (NULL != sigs); - for (unsigned int i = 0; i<wc->planchets_length; i++) - { - struct PlanchetContext *pc = &wc->planchets[i]; - - GNUNET_assert ( - 0 == - json_array_append_new ( - sigs, - GNUNET_JSON_PACK ( - TALER_JSON_pack_blinded_denom_sig ( - "ev_sig", - &pc->collectable.sig)))); - } - DH_METRICS_batch_withdraw_num_coins += wc->planchets_length; - return TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - GNUNET_JSON_pack_array_steal ("ev_sigs", - sigs)); -} - - -/** - * Check if the @a wc is replayed and we already have an - * answer. If so, replay the existing answer and return the - * HTTP response. - * - * @param wc parsed request data - * @param[out] mret HTTP status, set if we return true - * @return true if the request is idempotent with an existing request - * false if we did not find the request in the DB and did not set @a mret - */ -static bool -check_request_idempotent (const struct BatchWithdrawContext *wc, - MHD_RESULT *mret) -{ - const struct DH_RequestContext *rc = wc->rc; - - for (unsigned int i = 0; i<wc->planchets_length; i++) - { - struct PlanchetContext *pc = &wc->planchets[i]; - enum GNUNET_DB_QueryStatus qs; - - qs = DH_plugin->get_withdraw_info (DH_plugin->cls, - &pc->h_coin_envelope, - &pc->collectable); - if (0 > qs) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - *mret = TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "get_withdraw_info"); - return true; /* well, kind-of */ - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - return false; - } - /* generate idempotent reply */ - DH_METRICS_num_requests[DH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW]++; - *mret = generate_reply_success (rc, - wc); - return true; -} - - -/** - * Function implementing withdraw transaction. Runs the - * transaction logic; IF it returns a non-error code, the transaction - * logic MUST NOT queue a MHD response. IF it returns an hard error, - * the transaction logic MUST queue a MHD response and set @a mhd_ret. - * IF it returns the soft error code, the function MAY be called again - * to retry and MUST not queue a MHD response. - * - * Note that "wc->collectable.sig" is set before entering this function as we - * signed before entering the transaction. - * - * @param cls a `struct BatchWithdrawContext *` - * @param connection MHD request which triggered the transaction - * @param[out] mhd_ret set to MHD response status for @a connection, - * if transaction failed (!) - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -batch_withdraw_transaction (void *cls, - struct MHD_Connection *connection, - MHD_RESULT *mhd_ret) -{ - struct BatchWithdrawContext *wc = cls; - uint64_t ruuid; - enum GNUNET_DB_QueryStatus qs; - bool found = false; - bool balance_ok = false; - bool age_ok = false; - uint16_t allowed_maximum_age = 0; - char *kyc_required; - struct TALER_PaytoHashP reserve_h_payto; - - wc->now = GNUNET_TIME_timestamp_get (); - /* Do AML check: compute total merged amount and check - against applicable AML threshold */ - { - char *reserve_payto; - - reserve_payto = TALER_reserve_make_payto (DH_base_url, - wc->reserve_pub); - TALER_payto_hash (reserve_payto, - &reserve_h_payto); - GNUNET_free (reserve_payto); - } - { - struct TALER_Amount merge_amount; - struct TALER_Amount threshold; - struct GNUNET_TIME_Absolute now_minus_one_month; - - now_minus_one_month - = GNUNET_TIME_absolute_subtract (wc->now.abs_time, - GNUNET_TIME_UNIT_MONTHS); - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (DH_currency, - &merge_amount)); - qs = DH_plugin->select_merge_amounts_for_kyc_check (DH_plugin->cls, - &reserve_h_payto, - now_minus_one_month, - &aml_amount_cb, - &merge_amount); - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "select_merge_amounts_for_kyc_check"); - return qs; - } - qs = DH_plugin->select_aml_threshold (DH_plugin->cls, - &reserve_h_payto, - &wc->aml_decision, - &wc->kyc, - &threshold); - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "select_aml_threshold"); - return qs; - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - threshold = DH_aml_threshold; /* use default */ - wc->aml_decision = TALER_AML_NORMAL; - } - - switch (wc->aml_decision) - { - case TALER_AML_NORMAL: - if (0 >= TALER_amount_cmp (&merge_amount, - &threshold)) - { - /* merge_amount <= threshold, continue withdraw below */ - break; - } - wc->aml_decision = TALER_AML_PENDING; - qs = DH_plugin->trigger_aml_process (DH_plugin->cls, - &reserve_h_payto, - &merge_amount); - if (qs <= 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "trigger_aml_process"); - return qs; - } - return qs; - case TALER_AML_PENDING: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "AML already pending, doing nothing\n"); - return qs; - case TALER_AML_FROZEN: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Account frozen, doing nothing\n"); - return qs; - } - } - - /* Check if the money came from a wire transfer */ - qs = DH_plugin->reserves_get_origin (DH_plugin->cls, - wc->reserve_pub, - &wc->h_payto); - if (qs < 0) - { - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "reserves_get_origin"); - return qs; - } - /* If no results, reserve was created by merge, in which case no KYC check - is required as the merge already did that. */ - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - { - qs = TALER_KYCLOGIC_kyc_test_required ( - TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW, - &wc->h_payto, - DH_plugin->select_satisfied_kyc_processes, - DH_plugin->cls, - &batch_withdraw_amount_cb, - wc, - &kyc_required); - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "kyc_test_required"); - return qs; - } - if (NULL != kyc_required) - { - /* insert KYC requirement into DB! */ - wc->kyc.ok = false; - qs = DH_plugin->insert_kyc_requirement_for_account ( - DH_plugin->cls, - kyc_required, - &wc->h_payto, - wc->reserve_pub, - &wc->kyc.requirement_row); - GNUNET_free (kyc_required); - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_kyc_requirement_for_account"); - } - return qs; - } - } - wc->kyc.ok = true; - - qs = DH_plugin->do_batch_withdraw (DH_plugin->cls, - wc->now, - wc->reserve_pub, - &wc->batch_total, - DH_age_restriction_enabled, - &found, - &balance_ok, - &age_ok, - &allowed_maximum_age, - &ruuid); - if (0 > qs) - { - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "update_reserve_batch_withdraw"); - } - return qs; - } - if (! found) - { - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_DONAU_GENERIC_RESERVE_UNKNOWN, - NULL); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - if (! age_ok) - { - /* We respond with the lowest age in the corresponding age group - * of the required age */ - uint16_t lowest_age = TALER_get_lowest_age ( - &DH_age_restriction_config.mask, - allowed_maximum_age); - - DH_plugin->rollback (DH_plugin->cls); - *mhd_ret = DH_RESPONSE_reply_reserve_age_restriction_required ( - connection, - lowest_age); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - if (! balance_ok) - { - DH_plugin->rollback (DH_plugin->cls); - *mhd_ret = DH_RESPONSE_reply_reserve_insufficient_balance ( - connection, - TALER_EC_DONAU_WITHDRAW_INSUFFICIENT_FUNDS, - &wc->batch_total, - wc->reserve_pub); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - /* Add information about each planchet in the batch */ - for (unsigned int i = 0; i<wc->planchets_length; i++) - { - struct PlanchetContext *pc = &wc->planchets[i]; - const struct TALER_BlindedPlanchet *bp = &pc->blinded_planchet; - const struct TALER_CsNonce *nonce; - bool denom_unknown = true; - bool conflict = true; - bool nonce_reuse = true; - - nonce = (TALER_DENOMINATION_CS == bp->cipher) - ? &bp->details.cs_blinded_planchet.nonce - : NULL; - qs = DH_plugin->do_batch_withdraw_insert (DH_plugin->cls, - nonce, - &pc->collectable, - wc->now, - ruuid, - &denom_unknown, - &conflict, - &nonce_reuse); - if (0 > qs) - { - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "do_withdraw"); - return qs; - } - if (denom_unknown) - { - GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_INVARIANT_FAILURE, - NULL); - return GNUNET_DB_STATUS_HARD_ERROR; - } - if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) || - (conflict) ) - { - if (! check_request_idempotent (wc, - mhd_ret)) - { - /* We do not support *some* of the coins of the request being - idempotent while others being fresh. */ - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Idempotent coin in batch, not allowed. Aborting.\n"); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_CONFLICT, - TALER_EC_DONAU_WITHDRAW_BATCH_IDEMPOTENT_PLANCHET, - NULL); - } - return GNUNET_DB_STATUS_HARD_ERROR; - } - if (nonce_reuse) - { - GNUNET_break_op (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_DONAU_WITHDRAW_NONCE_REUSE, - NULL); - return GNUNET_DB_STATUS_HARD_ERROR; - } - } - DH_METRICS_num_success[DH_MT_SUCCESS_BATCH_WITHDRAW]++; - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; -} - - -/** - * The request was parsed successfully. Prepare - * our side for the main DB transaction. - * - * @param rc request details - * @param wc storage for request processing - * @return MHD result for the @a rc - */ -static MHD_RESULT -prepare_transaction (const struct DH_RequestContext *rc, - struct BatchWithdrawContext *wc) -{ - struct DH_CoinSignData csds[wc->planchets_length]; - struct TALER_BlindedDenominationSignature bss[wc->planchets_length]; - - for (unsigned int i = 0; i<wc->planchets_length; i++) - { - struct PlanchetContext *pc = &wc->planchets[i]; - - csds[i].h_denom_pub = &pc->collectable.denom_pub_hash; - csds[i].bp = &pc->blinded_planchet; - } - { - enum TALER_ErrorCode ec; - - ec = DH_keys_denomination_batch_sign ( - csds, - wc->planchets_length, - false, - bss); - if (TALER_EC_NONE != ec) - { - GNUNET_break (0); - return TALER_MHD_reply_with_ec (rc->connection, - ec, - NULL); - } - } - for (unsigned int i = 0; i<wc->planchets_length; i++) - { - struct PlanchetContext *pc = &wc->planchets[i]; - - pc->collectable.sig = bss[i]; - } - - /* run transaction */ - { - MHD_RESULT mhd_ret; - - if (GNUNET_OK != - DH_DB_run_transaction (rc->connection, - "run batch withdraw", - DH_MT_REQUEST_WITHDRAW, - &mhd_ret, - &batch_withdraw_transaction, - wc)) - { - return mhd_ret; - } - } - /* return final positive response */ - return generate_reply_success (rc, - wc); -} - - -/** - * Continue processing the request @a rc by parsing the - * @a planchets and then running the transaction. - * - * @param rc request details - * @param wc storage for request processing - * @param planchets array of planchets to parse - * @return MHD result for the @a rc - */ -static MHD_RESULT -parse_planchets (const struct DH_RequestContext *rc, - struct BatchWithdrawContext *wc, - const json_t *planchets) -{ - struct DH_KeyStateHandle *ksh; - MHD_RESULT mret; - - for (unsigned int i = 0; i<wc->planchets_length; i++) - { - struct PlanchetContext *pc = &wc->planchets[i]; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_fixed_auto ("reserve_sig", - &pc->collectable.reserve_sig), - GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", - &pc->collectable.denom_pub_hash), - TALER_JSON_spec_blinded_planchet ("coin_ev", - &pc->blinded_planchet), - GNUNET_JSON_spec_end () - }; - - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (rc->connection, - json_array_get (planchets, - i), - ispec); - if (GNUNET_OK != res) - return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; - } - pc->collectable.reserve_pub = *wc->reserve_pub; - for (unsigned int k = 0; k<i; k++) - { - const struct PlanchetContext *kpc = &wc->planchets[k]; - - if (0 == - TALER_blinded_planchet_cmp (&kpc->blinded_planchet, - &pc->blinded_planchet)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "duplicate planchet"); - } - } - } - - ksh = DH_keys_get_state (); - if (NULL == ksh) - { - if (! check_request_idempotent (wc, - &mret)) - { - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_DONAU_GENERIC_KEYS_MISSING, - NULL); - } - return mret; - } - for (unsigned int i = 0; i<wc->planchets_length; i++) - { - struct PlanchetContext *pc = &wc->planchets[i]; - struct DH_DenominationKey *dk; - - dk = DH_keys_denomination_by_hash_from_state ( - ksh, - &pc->collectable.denom_pub_hash, - NULL, - NULL); - - if (NULL == dk) - { - if (! check_request_idempotent (wc, - &mret)) - { - return DH_RESPONSE_reply_unknown_denom_pub_hash ( - rc->connection, - &pc->collectable.denom_pub_hash); - } - return mret; - } - if (GNUNET_TIME_absolute_is_past (dk->meta.expire_withdraw.abs_time)) - { - /* This denomination is past the expiration time for withdraws */ - if (! check_request_idempotent (wc, - &mret)) - { - return DH_RESPONSE_reply_expired_denom_pub_hash ( - rc->connection, - &pc->collectable.denom_pub_hash, - TALER_EC_DONAU_GENERIC_DENOMINATION_EXPIRED, - "WITHDRAW"); - } - return mret; - } - if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) - { - /* This denomination is not yet valid, no need to check - for idempotency! */ - return DH_RESPONSE_reply_expired_denom_pub_hash ( - rc->connection, - &pc->collectable.denom_pub_hash, - TALER_EC_DONAU_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - "WITHDRAW"); - } - if (dk->recoup_possible) - { - /* This denomination has been revoked */ - if (! check_request_idempotent (wc, - &mret)) - { - return DH_RESPONSE_reply_expired_denom_pub_hash ( - rc->connection, - &pc->collectable.denom_pub_hash, - TALER_EC_DONAU_GENERIC_DENOMINATION_REVOKED, - "WITHDRAW"); - } - return mret; - } - if (dk->denom_pub.cipher != pc->blinded_planchet.cipher) - { - /* denomination cipher and blinded planchet cipher not the same */ - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_DONAU_GENERIC_CIPHER_MISMATCH, - NULL); - } - if (0 > - TALER_amount_add (&pc->collectable.amount_with_fee, - &dk->meta.value, - &dk->meta.fees.withdraw)) - { - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_DONAU_WITHDRAW_AMOUNT_FEE_OVERFLOW, - NULL); - } - if (0 > - TALER_amount_add (&wc->batch_total, - &wc->batch_total, - &pc->collectable.amount_with_fee)) - { - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_DONAU_WITHDRAW_AMOUNT_FEE_OVERFLOW, - NULL); - } - - if (GNUNET_OK != - TALER_coin_ev_hash (&pc->blinded_planchet, - &pc->collectable.denom_pub_hash, - &pc->collectable.h_coin_envelope)) - { - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - NULL); - } - DH_METRICS_num_verifications[DH_MT_SIGNATURE_EDDSA]++; - if (GNUNET_OK != - TALER_wallet_withdraw_verify (&pc->collectable.denom_pub_hash, - &pc->collectable.amount_with_fee, - &pc->collectable.h_coin_envelope, - &pc->collectable.reserve_pub, - &pc->collectable.reserve_sig)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_DONAU_WITHDRAW_RESERVE_SIGNATURE_INVALID, - NULL); - } - } - /* everything parsed */ - return prepare_transaction (rc, - wc); -} - - -MHD_RESULT -DH_handler_batch_withdraw (struct DH_RequestContext *rc, - const struct TALER_ReservePublicKeyP *reserve_pub, - const json_t *root) -{ - struct BatchWithdrawContext wc = { - .reserve_pub = reserve_pub, - .rc = rc - }; - const json_t *planchets; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("planchets", - &planchets), - GNUNET_JSON_spec_end () - }; - - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (DH_currency, - &wc.batch_total)); - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (rc->connection, - root, - spec); - if (GNUNET_OK != res) - return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; - } - wc.planchets_length = json_array_size (planchets); - if (0 == wc.planchets_length) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "planchets"); - } - if (wc.planchets_length > TALER_MAX_FRESH_COINS) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "too many planchets"); - } - { - struct PlanchetContext splanchets[wc.planchets_length]; - MHD_RESULT ret; - - memset (splanchets, - 0, - sizeof (splanchets)); - wc.planchets = splanchets; - ret = parse_planchets (rc, - &wc, - planchets); - /* Clean up */ - for (unsigned int i = 0; i<wc.planchets_length; i++) - { - struct PlanchetContext *pc = &wc.planchets[i]; - - TALER_blinded_planchet_free (&pc->blinded_planchet); - TALER_blinded_denom_sig_free (&pc->collectable.sig); - } - return ret; - } -} - - -/* end of donau-httpd_batch-issue_receipts.c */ diff --git a/src/donau/donau-httpd_batch-issue_receipts.h b/src/donau/donau-httpd_batch-issue_receipts.h @@ -1,48 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-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 CHARITYABILITY 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_batch-issue_receipts.h - * @brief Handle /reserve/batch-withdraw requests - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#ifndef DONAU_HTTPD_BATCH_WITHDRAW_H -#define DONAU_HTTPD_BATCH_WITHDRAW_H - -#include <microhttpd.h> -#include "taler-donau-httpd.h" - - -/** - * Handle a "/reserves/$RESERVE_PUB/batch-withdraw" request. Parses the batch of - * requested "denom_pub" which specifies the key/value of the coin to be - * withdrawn, and checks that the signature "reserve_sig" makes this a valid - * withdrawal request from the specified reserve. If so, the envelope with - * the blinded coin "coin_ev" is passed down to execute the withdrawal - * operation. - * - * @param rc request context - * @param root uploaded JSON data - * @param reserve_pub public key of the reserve - * @return MHD result code - */ -MHD_RESULT -DH_handler_batch_withdraw (struct DH_RequestContext *rc, - const struct TALER_ReservePublicKeyP *reserve_pub, - const json_t *root); - -#endif diff --git a/src/donau/donau-httpd_charities_close.c b/src/donau/donau-httpd_charities_close.c @@ -1,448 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2022 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 CHARITYABILITY 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-donau-httpd_reserves_close.c - * @brief Handle /reserves/$RESERVE_PUB/close requests - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include "taler_kyclogic_lib.h" -#include "taler/taler_mhd_lib.h" -#include "taler/taler_json_lib.h" -#include "taler_dbevents.h" -#include "taler-donau-httpd_keys.h" -#include "taler-donau-httpd_reserves_close.h" -#include "taler-donau-httpd_responses.h" - - -/** - * How far do we allow a client's time to be off when - * checking the request timestamp? - */ -#define TIMESTAMP_TOLERANCE \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15) - - -/** - * Closure for #reserve_close_transaction. - */ -struct ReserveCloseContext -{ - /** - * Public key of the reserve the inquiry is about. - */ - const struct TALER_ReservePublicKeyP *reserve_pub; - - /** - * Timestamp of the request. - */ - struct GNUNET_TIME_Timestamp timestamp; - - /** - * Client signature approving the request. - */ - struct TALER_ReserveSignatureP reserve_sig; - - /** - * Amount that will be wired (after closing fees). - */ - struct TALER_Amount wire_amount; - - /** - * Current balance of the reserve. - */ - struct TALER_Amount balance; - - /** - * Where to wire the funds, may be NULL. - */ - const char *payto_uri; - - /** - * Hash of the @e payto_uri, if given (otherwise zero). - */ - struct TALER_PaytoHashP h_payto; - - /** - * KYC status for the request. - */ - struct DONAUDB_KycStatus kyc; - - /** - * Hash of the payto-URI that was used for the KYC decision. - */ - struct TALER_PaytoHashP kyc_payto; - - /** - * Query status from the amount_it() helper function. - */ - enum GNUNET_DB_QueryStatus qs; -}; - - -/** - * Send reserve close to client. - * - * @param connection connection to the client - * @param rhc reserve close to return - * @return MHD result code - */ -static MHD_RESULT -reply_reserve_close_success (struct MHD_Connection *connection, - const struct ReserveCloseContext *rhc) -{ - return TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_OK, - TALER_JSON_pack_amount ("wire_amount", - &rhc->wire_amount)); -} - - -/** - * Function called to iterate over KYC-relevant - * transaction amounts for a particular time range. - * Called within a database transaction, so must - * not start a new one. - * - * @param cls closure, identifies the event type and - * account to iterate over events for - * @param limit maximum time-range for which events - * should be fetched (timestamp in the past) - * @param cb function to call on each event found, - * events must be returned in reverse chronological - * order - * @param cb_cls closure for @a cb - */ -static void -amount_it (void *cls, - struct GNUNET_TIME_Absolute limit, - DONAUDB_KycAmountCallback cb, - void *cb_cls) -{ - struct ReserveCloseContext *rcc = cls; - enum GNUNET_GenericReturnValue ret; - - ret = cb (cb_cls, - &rcc->balance, - GNUNET_TIME_absolute_get ()); - GNUNET_break (GNUNET_SYSERR != ret); - if (GNUNET_OK != ret) - return; - rcc->qs - = DH_plugin->iterate_reserve_close_info ( - DH_plugin->cls, - &rcc->kyc_payto, - limit, - cb, - cb_cls); -} - - -/** - * Function implementing /reserves/$RID/close transaction. Given the public - * key of a reserve, return the associated transaction close. Runs the - * transaction logic; IF it returns a non-error code, the transaction logic - * MUST NOT queue a MHD response. IF it returns an hard error, the - * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it - * returns the soft error code, the function MAY be called again to retry and - * MUST not queue a MHD response. - * - * @param cls a `struct ReserveCloseContext *` - * @param connection MHD request which triggered the transaction - * @param[out] mhd_ret set to MHD response status for @a connection, - * if transaction failed (!); unused - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -reserve_close_transaction (void *cls, - struct MHD_Connection *connection, - MHD_RESULT *mhd_ret) -{ - struct ReserveCloseContext *rcc = cls; - enum GNUNET_DB_QueryStatus qs; - char *payto_uri = NULL; - const struct TALER_WireFeeSet *wf; - - qs = DH_plugin->select_reserve_close_info ( - DH_plugin->cls, - rcc->reserve_pub, - &rcc->balance, - &payto_uri); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - *mhd_ret - = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "select_reserve_close_info"); - return qs; - case GNUNET_DB_STATUS_SOFT_ERROR: - return qs; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - *mhd_ret - = TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_DONAU_GENERIC_RESERVE_UNKNOWN, - NULL); - return GNUNET_DB_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - - if ( (NULL == rcc->payto_uri) && - (NULL == payto_uri) ) - { - *mhd_ret - = TALER_MHD_reply_with_error (connection, - MHD_HTTP_CONFLICT, - TALER_EC_DONAU_RESERVES_CLOSE_NO_TARGET_ACCOUNT, - NULL); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - if ( (NULL != rcc->payto_uri) && - ( (NULL == payto_uri) || - (0 != strcmp (payto_uri, - rcc->payto_uri)) ) ) - { - /* KYC check may be needed: we're not returning - the money to the account that funded the reserve - in the first place. */ - char *kyc_needed; - - TALER_payto_hash (rcc->payto_uri, - &rcc->kyc_payto); - rcc->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; - qs = TALER_KYCLOGIC_kyc_test_required ( - TALER_KYCLOGIC_KYC_TRIGGER_RESERVE_CLOSE, - &rcc->kyc_payto, - DH_plugin->select_satisfied_kyc_processes, - DH_plugin->cls, - &amount_it, - rcc, - &kyc_needed); - if (qs < 0) - { - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - return qs; - GNUNET_break (0); - *mhd_ret - = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "iterate_reserve_close_info"); - return qs; - } - if (rcc->qs < 0) - { - if (GNUNET_DB_STATUS_SOFT_ERROR == rcc->qs) - return rcc->qs; - GNUNET_break (0); - *mhd_ret - = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "iterate_reserve_close_info"); - return qs; - } - if (NULL != kyc_needed) - { - rcc->kyc.ok = false; - qs = DH_plugin->insert_kyc_requirement_for_account ( - DH_plugin->cls, - kyc_needed, - &rcc->kyc_payto, - rcc->reserve_pub, - &rcc->kyc.requirement_row); - GNUNET_free (kyc_needed); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - GNUNET_break (0); - *mhd_ret - = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_kyc_requirement_for_account"); - } - return qs; - } - } - - rcc->kyc.ok = true; - if (NULL == rcc->payto_uri) - rcc->payto_uri = payto_uri; - - { - char *method; - - method = TALER_payto_get_method (rcc->payto_uri); - wf = DH_wire_fees_by_time (rcc->timestamp, - method); - if (NULL == wf) - { - GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_DONAU_WIRE_FEES_NOT_CONFIGURED, - method); - GNUNET_free (method); - return GNUNET_DB_STATUS_HARD_ERROR; - } - GNUNET_free (method); - } - - if (0 > - TALER_amount_subtract (&rcc->wire_amount, - &rcc->balance, - &wf->closing)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Client attempted to close reserve with insufficient balance.\n"); - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (DH_currency, - &rcc->wire_amount)); - *mhd_ret = reply_reserve_close_success (connection, - rcc); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - qs = DH_plugin->insert_close_request (DH_plugin->cls, - rcc->reserve_pub, - payto_uri, - &rcc->reserve_sig, - rcc->timestamp, - &rcc->balance, - &wf->closing); - GNUNET_free (payto_uri); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - GNUNET_break (0); - *mhd_ret - = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "insert_close_request"); - return qs; - } - if (qs <= 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - return qs; - } - return qs; -} - - -MHD_RESULT -DH_handler_reserves_close (struct DH_RequestContext *rc, - const struct TALER_ReservePublicKeyP *reserve_pub, - const json_t *root) -{ - struct ReserveCloseContext rcc = { - .payto_uri = NULL, - .reserve_pub = reserve_pub - }; - MHD_RESULT mhd_ret; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_timestamp ("request_timestamp", - &rcc.timestamp), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("payto_uri", - &rcc.payto_uri), - NULL), - GNUNET_JSON_spec_fixed_auto ("reserve_sig", - &rcc.reserve_sig), - GNUNET_JSON_spec_end () - }; - - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (rc->connection, - root, - spec); - if (GNUNET_SYSERR == res) - { - GNUNET_break (0); - return MHD_NO; /* hard failure */ - } - if (GNUNET_NO == res) - { - GNUNET_break_op (0); - return MHD_YES; /* failure */ - } - } - - { - struct GNUNET_TIME_Timestamp now; - - now = GNUNET_TIME_timestamp_get (); - if (! GNUNET_TIME_absolute_approx_eq (now.abs_time, - rcc.timestamp.abs_time, - TIMESTAMP_TOLERANCE)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_DONAU_GENERIC_CLOCK_SKEW, - NULL); - } - } - - if (NULL != rcc.payto_uri) - TALER_payto_hash (rcc.payto_uri, - &rcc.h_payto); - if (GNUNET_OK != - TALER_wallet_reserve_close_verify (rcc.timestamp, - &rcc.h_payto, - reserve_pub, - &rcc.reserve_sig)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_DONAU_RESERVES_CLOSE_BAD_SIGNATURE, - NULL); - } - - if (GNUNET_OK != - DH_DB_run_transaction (rc->connection, - "reserve close", - DH_MT_REQUEST_OTHER, - &mhd_ret, - &reserve_close_transaction, - &rcc)) - { - return mhd_ret; - } - if (! rcc.kyc.ok) - return DH_RESPONSE_reply_kyc_required (rc->connection, - &rcc.kyc_payto, - &rcc.kyc); - - return reply_reserve_close_success (rc->connection, - &rcc); -} - - -/* end of taler-donau-httpd_reserves_close.c */ diff --git a/src/donau/donau-httpd_charities_close.h b/src/donau/donau-httpd_charities_close.h @@ -1,41 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 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 CHARITYABILITY 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-donau-httpd_reserves_close.h - * @brief Handle /reserves/$RESERVE_PUB/close requests - * @author Christian Grothoff - */ -#ifndef DONAU_HTTPD_RESERVES_CLOSE_H -#define DONAU_HTTPD_RESERVES_CLOSE_H - -#include <microhttpd.h> -#include "taler-donau-httpd.h" - - -/** - * Handle a POST "/reserves/$RID/close" request. - * - * @param rc request context - * @param reserve_pub public key of the reserve - * @param root uploaded body from the client - * @return MHD result code - */ -MHD_RESULT -DH_handler_reserves_close (struct DH_RequestContext *rc, - const struct TALER_ReservePublicKeyP *reserve_pub, - const json_t *root); - -#endif diff --git a/src/donau/donau-httpd_common_submit_receipts.c b/src/donau/donau-httpd_common_submit_receipts.c @@ -1,267 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 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 CHARITYABILITY 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-donau-httpd_common_deposit.c - * @brief shared logic for handling deposited coins - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include "taler-donau-httpd_common_deposit.h" -#include "taler-donau-httpd.h" -#include "taler-donau-httpd_keys.h" - - -enum GNUNET_GenericReturnValue -DH_common_purse_deposit_parse_coin ( - struct MHD_Connection *connection, - struct DH_PurseDepositedCoin *coin, - const json_t *jcoin) -{ - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount ("amount", - DH_currency, - &coin->amount), - GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", - &coin->cpi.denom_pub_hash), - TALER_JSON_spec_denom_sig ("ub_sig", - &coin->cpi.denom_sig), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_fixed_auto ("attest", - &coin->attest), - &coin->no_attest), - GNUNET_JSON_spec_mark_optional ( - TALER_JSON_spec_age_commitment ("age_commitment", - &coin->age_commitment), - &coin->cpi.no_age_commitment), - GNUNET_JSON_spec_fixed_auto ("coin_sig", - &coin->coin_sig), - GNUNET_JSON_spec_fixed_auto ("coin_pub", - &coin->cpi.coin_pub), - GNUNET_JSON_spec_end () - }; - - memset (coin, - 0, - sizeof (*coin)); - coin->cpi.no_age_commitment = true; - coin->no_attest = true; - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (connection, - jcoin, - spec); - if (GNUNET_OK != res) - return res; - } - - /* check denomination exists and is valid */ - { - struct DH_DenominationKey *dk; - MHD_RESULT mret; - - dk = DH_keys_denomination_by_hash (&coin->cpi.denom_pub_hash, - connection, - &mret); - if (NULL == dk) - { - GNUNET_JSON_parse_free (spec); - return (MHD_YES == mret) ? GNUNET_NO : GNUNET_SYSERR; - } - if (! coin->cpi.no_age_commitment) - { - coin->age_commitment.mask = dk->meta.age_mask; - TALER_age_commitment_hash (&coin->age_commitment, - &coin->cpi.h_age_commitment); - } - if (0 > TALER_amount_cmp (&dk->meta.value, - &coin->amount)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return (MHD_YES == - TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_DONAU_GENERIC_AMOUNT_EXCEEDS_DENOMINATION_VALUE, - NULL)) - ? GNUNET_NO : GNUNET_SYSERR; - } - if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time)) - { - /* This denomination is past the expiration time for deposits */ - GNUNET_JSON_parse_free (spec); - return (MHD_YES == - DH_RESPONSE_reply_expired_denom_pub_hash ( - connection, - &coin->cpi.denom_pub_hash, - TALER_EC_DONAU_GENERIC_DENOMINATION_EXPIRED, - "PURSE CREATE")) - ? GNUNET_NO : GNUNET_SYSERR; - } - if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) - { - /* This denomination is not yet valid */ - GNUNET_JSON_parse_free (spec); - return (MHD_YES == - DH_RESPONSE_reply_expired_denom_pub_hash ( - connection, - &coin->cpi.denom_pub_hash, - TALER_EC_DONAU_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - "PURSE CREATE")) - ? GNUNET_NO : GNUNET_SYSERR; - } - if (dk->recoup_possible) - { - /* This denomination has been revoked */ - GNUNET_JSON_parse_free (spec); - return (MHD_YES == - DH_RESPONSE_reply_expired_denom_pub_hash ( - connection, - &coin->cpi.denom_pub_hash, - TALER_EC_DONAU_GENERIC_DENOMINATION_REVOKED, - "PURSE CREATE")) - ? GNUNET_NO : GNUNET_SYSERR; - } - if (dk->denom_pub.cipher != coin->cpi.denom_sig.cipher) - { - /* denomination cipher and denomination signature cipher not the same */ - GNUNET_JSON_parse_free (spec); - return (MHD_YES == - TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_DONAU_GENERIC_CIPHER_MISMATCH, - NULL)) - ? GNUNET_NO : GNUNET_SYSERR; - } - - coin->deposit_fee = dk->meta.fees.deposit; - if (0 < TALER_amount_cmp (&coin->deposit_fee, - &coin->amount)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_DONAU_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE, - NULL); - } - GNUNET_assert (0 <= - TALER_amount_subtract (&coin->amount_minus_fee, - &coin->amount, - &coin->deposit_fee)); - - /* check coin signature */ - switch (dk->denom_pub.cipher) - { - case TALER_DENOMINATION_RSA: - DH_METRICS_num_verifications[DH_MT_SIGNATURE_RSA]++; - break; - case TALER_DENOMINATION_CS: - DH_METRICS_num_verifications[DH_MT_SIGNATURE_CS]++; - break; - default: - break; - } - if (GNUNET_YES != - TALER_test_coin_valid (&coin->cpi, - &dk->denom_pub)) - { - TALER_LOG_WARNING ("Invalid coin passed for /deposit\n"); - GNUNET_JSON_parse_free (spec); - return (MHD_YES == - TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_DONAU_DENOMINATION_SIGNATURE_INVALID, - NULL)) - ? GNUNET_NO : GNUNET_SYSERR; - } - } - return GNUNET_OK; -} - - -enum GNUNET_GenericReturnValue -DH_common_deposit_check_purse_deposit ( - struct MHD_Connection *connection, - const struct DH_PurseDepositedCoin *coin, - const struct TALER_PurseContractPublicKeyP *purse_pub, - uint32_t min_age) -{ - if (GNUNET_OK != - TALER_wallet_purse_deposit_verify (DH_base_url, - purse_pub, - &coin->amount, - &coin->cpi.denom_pub_hash, - &coin->cpi.h_age_commitment, - &coin->cpi.coin_pub, - &coin->coin_sig)) - { - TALER_LOG_WARNING ( - "Invalid coin signature to deposit into purse\n"); - return (MHD_YES == - TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_DONAU_PURSE_DEPOSIT_COIN_SIGNATURE_INVALID, - DH_base_url)) - ? GNUNET_NO - : GNUNET_SYSERR; - } - - if (0 == min_age) - return GNUNET_OK; /* no need to apply age checks */ - - /* Check and verify the age restriction. */ - if (coin->no_attest != coin->cpi.no_age_commitment) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_DONAU_PURSE_DEPOSIT_COIN_CONFLICTING_ATTEST_VS_AGE_COMMITMENT, - "mismatch of attest and age_commitment"); - } - - if (coin->cpi.no_age_commitment) - return GNUNET_OK; /* unrestricted coin */ - - /* age attestation must be valid */ - if (GNUNET_OK != - TALER_age_commitment_verify (&coin->age_commitment, - min_age, - &coin->attest)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_DONAU_PURSE_DEPOSIT_COIN_AGE_ATTESTATION_FAILURE, - "invalid attest for minimum age"); - } - return GNUNET_OK; -} - - -/** - * Release data structures of @a coin. Note that - * @a coin itself is NOT freed. - * - * @param[in] coin information to release - */ -void -DH_common_purse_deposit_free_coin (struct DH_PurseDepositedCoin *coin) -{ - TALER_denom_sig_free (&coin->cpi.denom_sig); - if (! coin->cpi.no_age_commitment) - GNUNET_free (coin->age_commitment.keys); /* Only the keys have been allocated */ -} diff --git a/src/donau/donau-httpd_common_submit_receipts.h b/src/donau/donau-httpd_common_submit_receipts.h @@ -1,130 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 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 CHARITYABILITY 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-donau-httpd_common_deposit.h - * @brief shared logic for handling deposited coins - * @author Christian Grothoff - */ -#ifndef DONAU_HTTPD_COMMON_DEPOSIT_H -#define DONAU_HTTPD_COMMON_DEPOSIT_H - -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include "taler/taler_json_lib.h" -#include "taler/taler_mhd_lib.h" - - -/** - * Information about an individual coin being deposited. - */ -struct DH_PurseDepositedCoin -{ - /** - * Public information about the coin. - */ - struct TALER_CoinPublicInfo cpi; - - /** - * Signature affirming spending the coin. - */ - struct TALER_CoinSpendSignatureP coin_sig; - - /** - * Amount to be put into the purse from this coin. - */ - struct TALER_Amount amount; - - /** - * Deposit fee applicable for this coin. - */ - struct TALER_Amount deposit_fee; - - /** - * Amount to be put into the purse from this coin. - */ - struct TALER_Amount amount_minus_fee; - - /** - * Age attestation provided, set if @e no_attest is false. - */ - struct TALER_AgeAttestation attest; - - /** - * Age commitment provided, set if @e cpi.no_age_commitment is false. - */ - struct TALER_AgeCommitment age_commitment; - - /** - * ID of the coin in known_coins. - */ - uint64_t known_coin_id; - - /** - * True if @e attest was not provided. - */ - bool no_attest; - -}; - - -/** - * Parse a coin and check signature of the coin and the denomination - * signature over the coin. - * - * @param[in,out] connection our HTTP connection - * @param[out] coin coin to initialize - * @param jcoin coin to parse - * @return #GNUNET_OK on success, #GNUNET_NO if an error was returned, - * #GNUNET_SYSERR on failure and no error could be returned - */ -enum GNUNET_GenericReturnValue -DH_common_purse_deposit_parse_coin ( - struct MHD_Connection *connection, - struct DH_PurseDepositedCoin *coin, - const json_t *jcoin); - - -/** - * Check that the deposited @a coin is valid for @a purse_pub - * and has a valid age commitment for @a min_age. - * - * @param[in,out] connection our HTTP connection - * @param coin the coin to evaluate - * @param purse_pub public key of the purse the coin was deposited into - * @param min_age minimum age restriction expected for this purse - * @return #GNUNET_OK on success, #GNUNET_NO if an error was returned, - * #GNUNET_SYSERR on failure and no error could be returned - */ -enum GNUNET_GenericReturnValue -DH_common_deposit_check_purse_deposit ( - struct MHD_Connection *connection, - const struct DH_PurseDepositedCoin *coin, - const struct TALER_PurseContractPublicKeyP *purse_pub, - uint32_t min_age); - - -/** - * Release data structures of @a coin. Note that - * @a coin itself is NOT freed. - * - * @param[in] coin information to release - */ -void -DH_common_purse_deposit_free_coin (struct DH_PurseDepositedCoin *coin); - -#endif diff --git a/src/donau/donau-httpd_config.c b/src/donau/donau-httpd_config.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015-2021 Taler Systems SA + Copyright (C) 2024 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 @@ -23,7 +23,6 @@ #include "taler_dbevents.h" #include "taler-donau-httpd_config.h" #include "taler/taler_json_lib.h" -#include "taler_kyclogic_lib.h" #include "taler/taler_mhd_lib.h" #include <jansson.h> @@ -37,8 +36,6 @@ DH_handler_config (struct DH_RequestContext *rc, if (NULL == resp) { resp = TALER_MHD_MAKE_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("supported_kyc_requirements", - TALER_KYCLOGIC_get_satisfiable ()), GNUNET_JSON_pack_string ("currency", DH_currency), GNUNET_JSON_pack_string ("name", diff --git a/src/donau/donau-httpd_db.c b/src/donau/donau-httpd_db.c @@ -29,61 +29,6 @@ #include "taler-donau-httpd_responses.h" -enum GNUNET_DB_QueryStatus -DH_make_coin_known (const struct TALER_CoinPublicInfo *coin, - struct MHD_Connection *connection, - uint64_t *known_coin_id, - MHD_RESULT *mhd_ret) -{ - enum DONAUDB_CoinKnownStatus cks; - struct TALER_DenominationHashP h_denom_pub; - struct TALER_AgeCommitmentHash age_hash; - - /* make sure coin is 'known' in database */ - cks = DH_plugin->ensure_coin_known (DH_plugin->cls, - coin, - known_coin_id, - &h_denom_pub, - &age_hash); - switch (cks) - { - case DONAUDB_CKS_ADDED: - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; - case DONAUDB_CKS_PRESENT: - return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; - case DONAUDB_CKS_SOFT_FAIL: - return GNUNET_DB_STATUS_SOFT_ERROR; - case DONAUDB_CKS_HARD_FAIL: - *mhd_ret - = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - NULL); - return GNUNET_DB_STATUS_HARD_ERROR; - case DONAUDB_CKS_DENOM_CONFLICT: - /* FIXME: insufficient_funds != denom conflict! See issue #7267, need new - * strategy for evidence gathering */ - *mhd_ret = DH_RESPONSE_reply_coin_insufficient_funds ( - connection, - TALER_EC_DONAU_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY, - &h_denom_pub, - &coin->coin_pub); - return GNUNET_DB_STATUS_HARD_ERROR; - case DONAUDB_CKS_AGE_CONFLICT: - /* FIXME: insufficient_funds != Age conflict! See issue #7267, need new - * strategy for evidence gathering */ - *mhd_ret = DH_RESPONSE_reply_coin_insufficient_funds ( - connection, - TALER_EC_DONAU_GENERIC_COIN_CONFLICTING_AGE_HASH, - &h_denom_pub, - &coin->coin_pub); - return GNUNET_DB_STATUS_HARD_ERROR; - } - GNUNET_assert (0); - return GNUNET_DB_STATUS_HARD_ERROR; -} - - enum GNUNET_GenericReturnValue DH_DB_run_transaction (struct MHD_Connection *connection, const char *name, diff --git a/src/donau/donau-httpd_issue_receipts.c b/src/donau/donau-httpd_issue_receipts.c @@ -1,700 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-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 CHARITYABILITY 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-donau-httpd_withdraw.c - * @brief Handle /reserves/$RESERVE_PUB/withdraw requests - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include "taler-donau-httpd.h" -#include "taler/taler_json_lib.h" -#include "taler_kyclogic_lib.h" -#include "taler/taler_mhd_lib.h" -#include "taler-donau-httpd_withdraw.h" -#include "taler-donau-httpd_responses.h" -#include "taler-donau-httpd_keys.h" - - -/** - * Context for #withdraw_transaction. - */ -struct WithdrawContext -{ - - /** - * Hash of the (blinded) message to be signed by the Donau. - */ - struct TALER_BlindedCoinHashP h_coin_envelope; - - /** - * Blinded planchet. - */ - struct TALER_BlindedPlanchet blinded_planchet; - - /** - * Set to the resulting signed coin data to be returned to the client. - */ - struct DONAUDB_CollectableBlindcoin collectable; - - /** - * KYC status for the operation. - */ - struct DONAUDB_KycStatus kyc; - - /** - * Hash of the payto-URI representing the account - * from which the money was put into the reserve. - */ - struct TALER_PaytoHashP h_account_payto; - - /** - * Current time for the DB transaction. - */ - struct GNUNET_TIME_Timestamp now; - - /** - * AML decision, #TALER_AML_NORMAL if we may proceed. - */ - enum TALER_AmlDecisionState aml_decision; - -}; - - -/** - * Function called to iterate over KYC-relevant - * transaction amounts for a particular time range. - * Called within a database transaction, so must - * not start a new one. - * - * @param cls closure, identifies the event type and - * account to iterate over events for - * @param limit maximum time-range for which events - * should be fetched (timestamp in the past) - * @param cb function to call on each event found, - * events must be returned in reverse chronological - * order - * @param cb_cls closure for @a cb - */ -static void -withdraw_amount_cb (void *cls, - struct GNUNET_TIME_Absolute limit, - DONAUDB_KycAmountCallback cb, - void *cb_cls) -{ - struct WithdrawContext *wc = cls; - enum GNUNET_DB_QueryStatus qs; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Signaling amount %s for KYC check\n", - TALER_amount2s (&wc->collectable.amount_with_fee)); - if (GNUNET_OK != - cb (cb_cls, - &wc->collectable.amount_with_fee, - wc->now.abs_time)) - return; - qs = DH_plugin->select_withdraw_amounts_for_kyc_check ( - DH_plugin->cls, - &wc->h_account_payto, - limit, - cb, - cb_cls); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Got %d additional transactions for this withdrawal and limit %llu\n", - qs, - (unsigned long long) limit.abs_value_us); - GNUNET_break (qs >= 0); -} - - -/** - * Function called on each @a amount that was found to - * be relevant for the AML check as it was merged into - * the reserve. - * - * @param cls `struct TALER_Amount *` to total up the amounts - * @param amount encountered transaction amount - * @param date when was the amount encountered - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to abort iteration - * #GNUNET_SYSERR on internal error (also abort itaration) - */ -static enum GNUNET_GenericReturnValue -aml_amount_cb ( - void *cls, - const struct TALER_Amount *amount, - struct GNUNET_TIME_Absolute date) -{ - struct TALER_Amount *total = cls; - - GNUNET_assert (0 <= - TALER_amount_add (total, - total, - amount)); - return GNUNET_OK; -} - - -/** - * Function implementing withdraw transaction. Runs the - * transaction logic; IF it returns a non-error code, the transaction - * logic MUST NOT queue a MHD response. IF it returns an hard error, - * the transaction logic MUST queue a MHD response and set @a mhd_ret. - * IF it returns the soft error code, the function MAY be called again - * to retry and MUST not queue a MHD response. - * - * Note that "wc->collectable.sig" is set before entering this function as we - * signed before entering the transaction. - * - * @param cls a `struct WithdrawContext *` - * @param connection MHD request which triggered the transaction - * @param[out] mhd_ret set to MHD response status for @a connection, - * if transaction failed (!) - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -withdraw_transaction (void *cls, - struct MHD_Connection *connection, - MHD_RESULT *mhd_ret) -{ - struct WithdrawContext *wc = cls; - enum GNUNET_DB_QueryStatus qs; - bool found = false; - bool balance_ok = false; - bool nonce_ok = false; - bool age_ok = false; - uint16_t allowed_maximum_age = 0; - uint64_t ruuid; - const struct TALER_CsNonce *nonce; - const struct TALER_BlindedPlanchet *bp; - struct TALER_PaytoHashP reserve_h_payto; - - wc->now = GNUNET_TIME_timestamp_get (); - /* Do AML check: compute total merged amount and check - against applicable AML threshold */ - { - char *reserve_payto; - - reserve_payto = TALER_reserve_make_payto (DH_base_url, - &wc->collectable.reserve_pub); - TALER_payto_hash (reserve_payto, - &reserve_h_payto); - GNUNET_free (reserve_payto); - } - { - struct TALER_Amount merge_amount; - struct TALER_Amount threshold; - struct GNUNET_TIME_Absolute now_minus_one_month; - - now_minus_one_month - = GNUNET_TIME_absolute_subtract (wc->now.abs_time, - GNUNET_TIME_UNIT_MONTHS); - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (DH_currency, - &merge_amount)); - qs = DH_plugin->select_merge_amounts_for_kyc_check (DH_plugin->cls, - &reserve_h_payto, - now_minus_one_month, - &aml_amount_cb, - &merge_amount); - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "select_merge_amounts_for_kyc_check"); - return qs; - } - qs = DH_plugin->select_aml_threshold (DH_plugin->cls, - &reserve_h_payto, - &wc->aml_decision, - &wc->kyc, - &threshold); - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "select_aml_threshold"); - return qs; - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - threshold = DH_aml_threshold; /* use default */ - wc->aml_decision = TALER_AML_NORMAL; - } - - switch (wc->aml_decision) - { - case TALER_AML_NORMAL: - if (0 >= TALER_amount_cmp (&merge_amount, - &threshold)) - { - /* merge_amount <= threshold, continue withdraw below */ - break; - } - wc->aml_decision = TALER_AML_PENDING; - qs = DH_plugin->trigger_aml_process (DH_plugin->cls, - &reserve_h_payto, - &merge_amount); - if (qs <= 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "trigger_aml_process"); - return qs; - } - return qs; - case TALER_AML_PENDING: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "AML already pending, doing nothing\n"); - return qs; - case TALER_AML_FROZEN: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Account frozen, doing nothing\n"); - return qs; - } - } - - /* Check if the money came from a wire transfer */ - qs = DH_plugin->reserves_get_origin (DH_plugin->cls, - &wc->collectable.reserve_pub, - &wc->h_account_payto); - if (qs < 0) - return qs; - /* If no results, reserve was created by merge, in which case no KYC check - is required as the merge already did that. */ - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - { - char *kyc_required; - - qs = TALER_KYCLOGIC_kyc_test_required ( - TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW, - &wc->h_account_payto, - DH_plugin->select_satisfied_kyc_processes, - DH_plugin->cls, - &withdraw_amount_cb, - wc, - &kyc_required); - if (qs < 0) - { - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "kyc_test_required"); - } - return qs; - } - if (NULL != kyc_required) - { - /* insert KYC requirement into DB! */ - wc->kyc.ok = false; - qs = DH_plugin->insert_kyc_requirement_for_account ( - DH_plugin->cls, - kyc_required, - &wc->h_account_payto, - &wc->collectable.reserve_pub, - &wc->kyc.requirement_row); - GNUNET_free (kyc_required); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_kyc_requirement_for_account"); - } - return qs; - } - } - wc->kyc.ok = true; - bp = &wc->blinded_planchet; - nonce = (TALER_DENOMINATION_CS == bp->cipher) - ? &bp->details.cs_blinded_planchet.nonce - : NULL; - qs = DH_plugin->do_withdraw (DH_plugin->cls, - nonce, - &wc->collectable, - wc->now, - DH_age_restriction_enabled, - &found, - &balance_ok, - &nonce_ok, - &age_ok, - &allowed_maximum_age, - &ruuid); - if (0 > qs) - { - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "do_withdraw"); - } - return qs; - } - if (! found) - { - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_DONAU_GENERIC_RESERVE_UNKNOWN, - NULL); - return GNUNET_DB_STATUS_HARD_ERROR; - } - if (! age_ok) - { - /* We respond with the lowest age in the corresponding age group - * of the required age */ - uint16_t lowest_age = TALER_get_lowest_age ( - &DH_age_restriction_config.mask, - allowed_maximum_age); - - DH_plugin->rollback (DH_plugin->cls); - *mhd_ret = DH_RESPONSE_reply_reserve_age_restriction_required ( - connection, - lowest_age); - return GNUNET_DB_STATUS_HARD_ERROR; - } - if (! balance_ok) - { - DH_plugin->rollback (DH_plugin->cls); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Balance insufficient for /withdraw\n"); - *mhd_ret = DH_RESPONSE_reply_reserve_insufficient_balance ( - connection, - TALER_EC_DONAU_WITHDRAW_INSUFFICIENT_FUNDS, - &wc->collectable.amount_with_fee, - &wc->collectable.reserve_pub); - return GNUNET_DB_STATUS_HARD_ERROR; - } - if (! nonce_ok) - { - DH_plugin->rollback (DH_plugin->cls); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_CONFLICT, - TALER_EC_DONAU_WITHDRAW_NONCE_REUSE, - NULL); - return GNUNET_DB_STATUS_HARD_ERROR; - } - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - DH_METRICS_num_success[DH_MT_SUCCESS_WITHDRAW]++; - return qs; -} - - -/** - * Check if the @a rc is replayed and we already have an - * answer. If so, replay the existing answer and return the - * HTTP response. - * - * @param rc request context - * @param[in,out] wc parsed request data - * @param[out] mret HTTP status, set if we return true - * @return true if the request is idempotent with an existing request - * false if we did not find the request in the DB and did not set @a mret - */ -static bool -check_request_idempotent (struct DH_RequestContext *rc, - struct WithdrawContext *wc, - MHD_RESULT *mret) -{ - enum GNUNET_DB_QueryStatus qs; - - qs = DH_plugin->get_withdraw_info (DH_plugin->cls, - &wc->h_coin_envelope, - &wc->collectable); - if (0 > qs) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - *mret = TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "get_withdraw_info"); - return true; /* well, kind-of */ - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - return false; - /* generate idempotent reply */ - DH_METRICS_num_requests[DH_MT_REQUEST_IDEMPOTENT_WITHDRAW]++; - *mret = TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - TALER_JSON_pack_blinded_denom_sig ("ev_sig", - &wc->collectable.sig)); - TALER_blinded_denom_sig_free (&wc->collectable.sig); - return true; -} - - -MHD_RESULT -DH_handler_withdraw (struct DH_RequestContext *rc, - const struct TALER_ReservePublicKeyP *reserve_pub, - const json_t *root) -{ - struct WithdrawContext wc; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("reserve_sig", - &wc.collectable.reserve_sig), - GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", - &wc.collectable.denom_pub_hash), - TALER_JSON_spec_blinded_planchet ("coin_ev", - &wc.blinded_planchet), - GNUNET_JSON_spec_end () - }; - enum TALER_ErrorCode ec; - struct DH_DenominationKey *dk; - - memset (&wc, - 0, - sizeof (wc)); - wc.collectable.reserve_pub = *reserve_pub; - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (rc->connection, - root, - spec); - if (GNUNET_OK != res) - return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; - } - { - MHD_RESULT mret; - struct DH_KeyStateHandle *ksh; - - ksh = DH_keys_get_state (); - if (NULL == ksh) - { - if (! check_request_idempotent (rc, - &wc, - &mret)) - { - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_DONAU_GENERIC_KEYS_MISSING, - NULL); - } - GNUNET_JSON_parse_free (spec); - return mret; - } - - dk = DH_keys_denomination_by_hash_from_state ( - ksh, - &wc.collectable.denom_pub_hash, - NULL, - NULL); - - if (NULL == dk) - { - if (! check_request_idempotent (rc, - &wc, - &mret)) - { - GNUNET_JSON_parse_free (spec); - return DH_RESPONSE_reply_unknown_denom_pub_hash ( - rc->connection, - &wc.collectable.denom_pub_hash); - } - GNUNET_JSON_parse_free (spec); - return mret; - } - if (GNUNET_TIME_absolute_is_past (dk->meta.expire_withdraw.abs_time)) - { - /* This denomination is past the expiration time for withdraws */ - if (! check_request_idempotent (rc, - &wc, - &mret)) - { - GNUNET_JSON_parse_free (spec); - return DH_RESPONSE_reply_expired_denom_pub_hash ( - rc->connection, - &wc.collectable.denom_pub_hash, - TALER_EC_DONAU_GENERIC_DENOMINATION_EXPIRED, - "WITHDRAW"); - } - GNUNET_JSON_parse_free (spec); - return mret; - } - if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) - { - /* This denomination is not yet valid, no need to check - for idempotency! */ - GNUNET_JSON_parse_free (spec); - return DH_RESPONSE_reply_expired_denom_pub_hash ( - rc->connection, - &wc.collectable.denom_pub_hash, - TALER_EC_DONAU_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - "WITHDRAW"); - } - if (dk->recoup_possible) - { - /* This denomination has been revoked */ - if (! check_request_idempotent (rc, - &wc, - &mret)) - { - GNUNET_JSON_parse_free (spec); - return DH_RESPONSE_reply_expired_denom_pub_hash ( - rc->connection, - &wc.collectable.denom_pub_hash, - TALER_EC_DONAU_GENERIC_DENOMINATION_REVOKED, - "WITHDRAW"); - } - GNUNET_JSON_parse_free (spec); - return mret; - } - if (dk->denom_pub.cipher != wc.blinded_planchet.cipher) - { - /* denomination cipher and blinded planchet cipher not the same */ - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_DONAU_GENERIC_CIPHER_MISMATCH, - NULL); - } - } - - if (0 > - TALER_amount_add (&wc.collectable.amount_with_fee, - &dk->meta.value, - &dk->meta.fees.withdraw)) - { - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_DONAU_WITHDRAW_AMOUNT_FEE_OVERFLOW, - NULL); - } - - if (GNUNET_OK != - TALER_coin_ev_hash (&wc.blinded_planchet, - &wc.collectable.denom_pub_hash, - &wc.collectable.h_coin_envelope)) - { - GNUNET_break (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - NULL); - } - - DH_METRICS_num_verifications[DH_MT_SIGNATURE_EDDSA]++; - if (GNUNET_OK != - TALER_wallet_withdraw_verify (&wc.collectable.denom_pub_hash, - &wc.collectable.amount_with_fee, - &wc.collectable.h_coin_envelope, - &wc.collectable.reserve_pub, - &wc.collectable.reserve_sig)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_DONAU_WITHDRAW_RESERVE_SIGNATURE_INVALID, - NULL); - } - - { - struct DH_CoinSignData csd = { - .h_denom_pub = &wc.collectable.denom_pub_hash, - .bp = &wc.blinded_planchet - }; - - /* Sign before transaction! */ - ec = DH_keys_denomination_sign ( - &csd, - false, - &wc.collectable.sig); - } - if (TALER_EC_NONE != ec) - { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to sign coin: %d\n", - ec); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_ec (rc->connection, - ec, - NULL); - } - - /* run transaction */ - { - MHD_RESULT mhd_ret; - - if (GNUNET_OK != - DH_DB_run_transaction (rc->connection, - "run withdraw", - DH_MT_REQUEST_WITHDRAW, - &mhd_ret, - &withdraw_transaction, - &wc)) - { - /* Even if #withdraw_transaction() failed, it may have created a signature - (or we might have done it optimistically above). */ - TALER_blinded_denom_sig_free (&wc.collectable.sig); - GNUNET_JSON_parse_free (spec); - return mhd_ret; - } - } - - /* Clean up and send back final response */ - GNUNET_JSON_parse_free (spec); - - if (! wc.kyc.ok) - return DH_RESPONSE_reply_kyc_required (rc->connection, - &wc.h_account_payto, - &wc.kyc); - - if (TALER_AML_NORMAL != wc.aml_decision) - return DH_RESPONSE_reply_aml_blocked (rc->connection, - wc.aml_decision); - - { - MHD_RESULT ret; - - ret = TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - TALER_JSON_pack_blinded_denom_sig ("ev_sig", - &wc.collectable.sig)); - TALER_blinded_denom_sig_free (&wc.collectable.sig); - return ret; - } -} - - -/* end of taler-donau-httpd_withdraw.c */ diff --git a/src/donau/donau-httpd_issue_receipts.h b/src/donau/donau-httpd_issue_receipts.h @@ -1,47 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2021 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 CHARITYABILITY 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-donau-httpd_withdraw.h - * @brief Handle /reserve/withdraw requests - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#ifndef DONAU_HTTPD_WITHDRAW_H -#define DONAU_HTTPD_WITHDRAW_H - -#include <microhttpd.h> -#include "taler-donau-httpd.h" - - -/** - * Handle a "/reserves/$RESERVE_PUB/withdraw" request. Parses the requested "denom_pub" which - * specifies the key/value of the coin to be withdrawn, and checks that the - * signature "reserve_sig" makes this a valid withdrawal request from the - * specified reserve. If so, the envelope with the blinded coin "coin_ev" is - * passed down to execute the withdrawal operation. - * - * @param rc request context - * @param root uploaded JSON data - * @param reserve_pub public key of the reserve - * @return MHD result code - */ -MHD_RESULT -DH_handler_withdraw (struct DH_RequestContext *rc, - const struct TALER_ReservePublicKeyP *reserve_pub, - const json_t *root); - -#endif diff --git a/src/donau/donau-httpd_keys.c b/src/donau/donau-httpd_keys.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020-2023 Taler Systems SA + Copyright (C) 2020-2024 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 @@ -23,7 +23,6 @@ #include "taler/taler_json_lib.h" #include "taler/taler_mhd_lib.h" #include "donau-httpd.h" -// #include "donau-httpd_config.h" #include "donau-httpd_keys.h" #include "donau-httpd_responses.h" #include "donaudb_plugin.h" @@ -68,11 +67,6 @@ struct HelperDonationUnit struct TALER_DenominationHashP h_denom_pub; /** - * Signature over this key from the security module's key. - */ - struct TALER_SecurityModuleSignatureP sm_sig; - - /** * The (full) public key. */ struct TALER_DenominationPublicKey denom_pub; @@ -109,16 +103,6 @@ struct HelperDonationUnit */ struct HelperSignkey { - /** - * When will the helper start to use this key for signing? - */ - // struct GNUNET_TIME_Timestamp start_time; - - /** - * For how long will the helper allow signing? 0 if - * the key was revoked or purged. - */ - // struct GNUNET_TIME_Relative validity_duration; /** * The public key. @@ -280,23 +264,6 @@ struct DH_KeyStateHandle uint64_t key_generation; /** - * When did we initiate the key reloading? - */ - // struct GNUNET_TIME_Timestamp reload_time; - - /** - * What is the period at which we rotate keys - * (signing or denomination keys)? - */ - // struct GNUNET_TIME_Relative rekey_frequency; - - /** - * When does our online signing key expire and we - * thus need to re-generate this response? - */ - // struct GNUNET_TIME_Timestamp signature_expires; - - /** * True if #finish_keys_response() was not yet run and this key state * is only suitable for the /management/keys API. */ @@ -579,212 +546,6 @@ destroy_key_helpers (struct HelperState *hs) /** - * Looks up the AGE_RESTRICTED setting for a denomination in the config and - * returns the age restriction (mask) accordingly. - * - * @param section_name Section in the configuration for the particular - * denomination. - */ -static struct TALER_AgeMask -load_age_mask (const char *section_name) -{ - static const struct TALER_AgeMask null_mask = {0}; - enum GNUNET_GenericReturnValue ret; - - if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value ( - DH_cfg, - section_name, - "AGE_RESTRICTED"))) - return null_mask; - - if (GNUNET_SYSERR == - (ret = GNUNET_CONFIGURATION_get_value_yesno (DH_cfg, - section_name, - "AGE_RESTRICTED"))) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - section_name, - "AGE_RESTRICTED", - "Value must be YES or NO\n"); - return null_mask; - } - - if (GNUNET_OK == ret) - { - if (! DH_age_restriction_enabled) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "age restriction set in section %s, yet, age restriction is not enabled\n", - section_name); - return DH_age_restriction_config.mask; - } - - - return null_mask; -} - - -/** - * Function called with information about available keys for signing. Usually - * only called once per key upon connect. Also called again in case a key is - * being revoked, in that case with an @a end_time of zero. - * - * @param cls closure with the `struct HelperState *` - * @param section_name name of the denomination type in the configuration; - * NULL if the key has been revoked or purged - * @param start_time when does the key become available for signing; - * zero if the key has been revoked or purged - * @param validity_duration how long does the key remain available for signing; - * zero if the key has been revoked or purged - * @param h_rsa hash of the @a denom_pub that is available (or was purged) - * @param denom_pub the public key itself, NULL if the key was revoked or purged - * @param sm_pub public key of the security module, NULL if the key was revoked or purged - * @param sm_sig signature from the security module, NULL if the key was revoked or purged - * The signature was already verified against @a sm_pub. - */ -static void -helper_rsa_cb ( - void *cls, - const char *section_name, - struct GNUNET_TIME_Timestamp start_time, - struct GNUNET_TIME_Relative validity_duration, - const struct TALER_RsaPubHashP *h_rsa, - const struct TALER_DenominationPublicKey *denom_pub, - const struct TALER_SecurityModulePublicKeyP *sm_pub, - const struct TALER_SecurityModuleSignatureP *sm_sig) -{ - struct HelperState *hs = cls; - struct HelperDonationUnit *hd; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "RSA helper announces key %s for denomination type %s with validity %s\n", - GNUNET_h2s (&h_rsa->hash), - section_name, - GNUNET_STRINGS_relative_time_to_string (validity_duration, - GNUNET_NO)); - key_generation++; - DH_resume_keys_requests (false); - hd = GNUNET_CONTAINER_multihashmap_get (hs->rsa_keys, - &h_rsa->hash); - if (NULL != hd) - { - /* should be just an update (revocation!), so update existing entry */ - hd->validity_duration = validity_duration; - return; - } - GNUNET_assert (NULL != sm_pub); - check_denom_rsa_sm_pub (sm_pub); - hd = GNUNET_new (struct HelperDonationUnit); - hd->start_time = start_time; - hd->validity_duration = validity_duration; - hd->h_details.h_rsa = *h_rsa; - hd->sm_sig = *sm_sig; - GNUNET_assert (TALER_DENOMINATION_RSA == denom_pub->cipher); - TALER_denom_pub_deep_copy (&hd->denom_pub, - denom_pub); - GNUNET_assert (TALER_DENOMINATION_RSA == hd->denom_pub.cipher); - /* load the age mask for the denomination, if applicable */ - hd->denom_pub.age_mask = load_age_mask (section_name); - TALER_denom_pub_hash (&hd->denom_pub, - &hd->h_denom_pub); - hd->section_name = GNUNET_strdup (section_name); - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multihashmap_put ( - hs->denom_keys, - &hd->h_denom_pub.hash, - hd, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multihashmap_put ( - hs->rsa_keys, - &hd->h_details.h_rsa.hash, - hd, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); -} - - -/** - * Function called with information about available CS keys for signing. Usually - * only called once per key upon connect. Also called again in case a key is - * being revoked, in that case with an @a end_time of zero. - * - * @param cls closure with the `struct HelperState *` - * @param section_name name of the denomination type in the configuration; - * NULL if the key has been revoked or purged - * @param start_time when does the key become available for signing; - * zero if the key has been revoked or purged - * @param validity_duration how long does the key remain available for signing; - * zero if the key has been revoked or purged - * @param h_cs hash of the @a denom_pub that is available (or was purged) - * @param denom_pub the public key itself, NULL if the key was revoked or purged - * @param sm_pub public key of the security module, NULL if the key was revoked or purged - * @param sm_sig signature from the security module, NULL if the key was revoked or purged - * The signature was already verified against @a sm_pub. - */ -static void -helper_cs_cb ( - void *cls, - const char *section_name, - struct GNUNET_TIME_Timestamp start_time, - struct GNUNET_TIME_Relative validity_duration, - const struct TALER_CsPubHashP *h_cs, - const struct TALER_DenominationPublicKey *denom_pub, - const struct TALER_SecurityModulePublicKeyP *sm_pub, - const struct TALER_SecurityModuleSignatureP *sm_sig) -{ - struct HelperState *hs = cls; - struct HelperDonationUnit *hd; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "CS helper announces key %s for denomination type %s with validity %s\n", - GNUNET_h2s (&h_cs->hash), - section_name, - GNUNET_STRINGS_relative_time_to_string (validity_duration, - GNUNET_NO)); - key_generation++; - DH_resume_keys_requests (false); - hd = GNUNET_CONTAINER_multihashmap_get (hs->cs_keys, - &h_cs->hash); - if (NULL != hd) - { - /* should be just an update (revocation!), so update existing entry */ - hd->validity_duration = validity_duration; - return; - } - GNUNET_assert (NULL != sm_pub); - check_denom_cs_sm_pub (sm_pub); - hd = GNUNET_new (struct HelperDonationUnit); - hd->start_time = start_time; - hd->validity_duration = validity_duration; - hd->h_details.h_cs = *h_cs; - hd->sm_sig = *sm_sig; - GNUNET_assert (TALER_DENOMINATION_CS == denom_pub->cipher); - TALER_denom_pub_deep_copy (&hd->denom_pub, - denom_pub); - /* load the age mask for the denomination, if applicable */ - hd->denom_pub.age_mask = load_age_mask (section_name); - TALER_denom_pub_hash (&hd->denom_pub, - &hd->h_denom_pub); - hd->section_name = GNUNET_strdup (section_name); - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multihashmap_put ( - hs->denom_keys, - &hd->h_denom_pub.hash, - hd, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multihashmap_put ( - hs->cs_keys, - &hd->h_details.h_cs.hash, - hd, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); -} - - -/** * Function called with information about available keys for signing. Usually * only called once per key upon connect. Also called again in case a key is * being revoked, in that case with an @a end_time of zero. @@ -1139,7 +900,6 @@ denomination_info_cb ( * @param cls closure with a `struct DH_KeyStateHandle *` * @param donau_pub the public key * @param meta meta data information about the denomination type (expirations) - * @param master_sig master signature affirming the validity of this denomination */ static void signkey_info_cb ( @@ -1218,8 +978,6 @@ add_sign_key_cb (void *cls, sk->meta.expire_sign), GNUNET_JSON_pack_timestamp ("stamp_end", sk->meta.expire_legal), - GNUNET_JSON_pack_data_auto ("master_sig", - &sk->master_sig), GNUNET_JSON_pack_data_auto ("key", &sk->donau_pub)))); return GNUNET_OK; @@ -1295,899 +1053,6 @@ add_denom_key_cb (void *cls, } -/** - * Add the headers we want to set for every /keys response. - * - * @param ksh the key state to use - * @param wsh wire state to use - * @param[in,out] response the response to modify - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -setup_general_response_headers (struct DH_KeyStateHandle *ksh, - struct WireStateHandle *wsh, - struct MHD_Response *response) -{ - char dat[128]; - - TALER_MHD_add_global_headers (response); - GNUNET_break (MHD_YES == - MHD_add_response_header (response, - MHD_HTTP_HEADER_CONTENT_TYPE, - "application/json")); - TALER_MHD_get_date_string (ksh->reload_time.abs_time, - dat); - GNUNET_break (MHD_YES == - MHD_add_response_header (response, - MHD_HTTP_HEADER_LAST_MODIFIED, - dat)); - if (! GNUNET_TIME_relative_is_zero (ksh->rekey_frequency)) - { - struct GNUNET_TIME_Relative r; - struct GNUNET_TIME_Absolute a; - struct GNUNET_TIME_Timestamp km; - struct GNUNET_TIME_Timestamp m; - struct GNUNET_TIME_Timestamp we; - - r = GNUNET_TIME_relative_min (DH_max_keys_caching, - ksh->rekey_frequency); - a = GNUNET_TIME_relative_to_absolute (r); - km = GNUNET_TIME_absolute_to_timestamp (a); - we = GNUNET_TIME_absolute_to_timestamp (wsh->cache_expiration); - m = GNUNET_TIME_timestamp_min (we, - km); - TALER_MHD_get_date_string (m.abs_time, - dat); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Setting /keys 'Expires' header to '%s' (rekey frequency is %s)\n", - dat, - GNUNET_TIME_relative2s (ksh->rekey_frequency, - false)); - GNUNET_break (MHD_YES == - MHD_add_response_header (response, - MHD_HTTP_HEADER_EXPIRES, - dat)); - ksh->signature_expires - = GNUNET_TIME_timestamp_min (m, - ksh->signature_expires); - } - /* Set cache control headers: our response varies depending on these headers */ - GNUNET_break (MHD_YES == - MHD_add_response_header (response, - MHD_HTTP_HEADER_VARY, - MHD_HTTP_HEADER_ACCEPT_ENCODING)); - /* Information is always public, revalidate after 1 hour */ - GNUNET_break (MHD_YES == - MHD_add_response_header (response, - MHD_HTTP_HEADER_CACHE_CONTROL, - "public,max-age=3600")); - return GNUNET_OK; -} - - -/** - * Function called with wallet balance thresholds. - * - * @param[in,out] cls a `json **` where to put the array of json amounts discovered - * @param threshold another threshold amount to add - */ -static void -wallet_threshold_cb (void *cls, - const struct TALER_Amount *threshold) -{ - json_t **ret = cls; - - if (NULL == *ret) - *ret = json_array (); - GNUNET_assert (0 == - json_array_append_new (*ret, - TALER_JSON_from_amount ( - threshold))); -} - - -/** - * Initialize @a krd using the given values for @a signkeys, - * @a recoup and @a denoms. - * - * @param[in,out] ksh key state handle we build @a krd for - * @param[in] denom_keys_hash hash over all the denomination keys in @a denoms - * @param last_cherry_pick_date timestamp to use - * @param[in,out] signkeys list of sign keys to return - * @param[in,out] recoup list of revoked keys to return - * @param[in,out] grouped_denominations list of grouped denominations to return - * @param h_grouped XOR of all hashes in @a grouped_demoninations - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -create_krd (struct DH_KeyStateHandle *ksh, - const struct GNUNET_HashCode *denom_keys_hash, - struct GNUNET_TIME_Timestamp last_cherry_pick_date, - json_t *signkeys, - json_t *recoup, - json_t *grouped_denominations, - const struct GNUNET_HashCode *h_grouped) -{ - struct KeysResponseData krd; - struct DONAU_DonauPublicKeyP donau_pub; - struct TALER_DonauSignatureP donau_sig; - struct DONAU_DonauPublicKeyP grouped_donau_pub; - struct TALER_DonauSignatureP grouped_donau_sig; - struct WireStateHandle *wsh; - json_t *keys; - - wsh = get_wire_state (); - if (MHD_HTTP_OK != wsh->http_status) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - GNUNET_assert (! GNUNET_TIME_absolute_is_zero ( - last_cherry_pick_date.abs_time)); - GNUNET_assert (NULL != signkeys); - GNUNET_assert (NULL != recoup); - GNUNET_assert (NULL != grouped_denominations); - GNUNET_assert (NULL != h_grouped); - GNUNET_assert (NULL != ksh->auditors); - GNUNET_assert (NULL != DH_currency); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Creating /keys at cherry pick date %s\n", - GNUNET_TIME_timestamp2s (last_cherry_pick_date)); - - /* Sign hash over denomination keys */ - { - enum TALER_ErrorCode ec; - - if (TALER_EC_NONE != - (ec = - TALER_donau_online_key_set_sign ( - &DH_keys_donau_sign2_, - ksh, - last_cherry_pick_date, - denom_keys_hash, - &donau_pub, - &donau_sig))) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Could not create key response data: cannot sign (%s)\n", - TALER_ErrorCode_get_hint (ec)); - return GNUNET_SYSERR; - } - } - - /* Sign grouped hash */ - { - enum TALER_ErrorCode ec; - - if (TALER_EC_NONE != - (ec = - TALER_donau_online_key_set_sign ( - &DH_keys_donau_sign2_, - ksh, - last_cherry_pick_date, - h_grouped, - &grouped_donau_pub, - &grouped_donau_sig))) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Could not create key response data: cannot sign grouped hash (%s)\n", - TALER_ErrorCode_get_hint (ec)); - return GNUNET_SYSERR; - } - } - - /* both public keys really must be the same */ - GNUNET_assert (0 == - memcmp (&grouped_donau_pub, - &donau_pub, - sizeof(donau_pub))); - - { - const struct SigningKey *sk; - - sk = GNUNET_CONTAINER_multipeermap_get ( - ksh->signkey_map, - (const struct GNUNET_PeerIdentity *) &donau_pub); - ksh->signature_expires = GNUNET_TIME_timestamp_min (sk->meta.expire_sign, - ksh->signature_expires); - } - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Build /keys data with %u wire accounts\n", - (unsigned int) json_array_size ( - json_object_get (wsh->json_reply, - "accounts"))); - - keys = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("version", - DONAU_PROTOCOL_VERSION), - GNUNET_JSON_pack_string ("base_url", - DH_base_url), - GNUNET_JSON_pack_string ("currency", - DH_currency), - GNUNET_JSON_pack_uint64 ("currency_fraction_digits", - DH_currency_fraction_digits), - GNUNET_JSON_pack_string ("asset_type", - asset_type), - GNUNET_JSON_pack_bool ("rewards_allowed", - GNUNET_YES == - DH_enable_rewards), - GNUNET_JSON_pack_data_auto ("master_public_key", - &DH_master_public_key), - GNUNET_JSON_pack_time_rel ("reserve_closing_delay", - DH_reserve_closing_delay), - GNUNET_JSON_pack_array_incref ("signkeys", - signkeys), - GNUNET_JSON_pack_array_incref ("recoup", - recoup), - GNUNET_JSON_pack_array_incref ("wads", - json_object_get (wsh->json_reply, - "wads")), - GNUNET_JSON_pack_array_incref ("accounts", - json_object_get (wsh->json_reply, - "accounts")), - GNUNET_JSON_pack_array_incref ("denominations", - grouped_denominations), - GNUNET_JSON_pack_timestamp ("list_issue_date", - last_cherry_pick_date), - GNUNET_JSON_pack_data_auto ("eddsa_pub", - &donau_pub), - GNUNET_JSON_pack_data_auto ("eddsa_sig", - &donau_sig), - GNUNET_JSON_pack_data_auto ("denominations_sig", - &grouped_donau_sig)); - GNUNET_assert (NULL != keys); - - { - char *keys_json; - void *keys_jsonz; - size_t keys_jsonz_size; - int comp; - char etag[sizeof (struct GNUNET_HashCode) * 2]; - - /* Convert /keys response to UTF8-String */ - keys_json = json_dumps (keys, - JSON_INDENT (2)); - json_decref (keys); - GNUNET_assert (NULL != keys_json); - - /* Keep copy for later compression... */ - keys_jsonz = GNUNET_strdup (keys_json); - keys_jsonz_size = strlen (keys_json); - - /* hash to compute etag */ - { - struct GNUNET_HashCode ehash; - char *end; - - GNUNET_CRYPTO_hash (keys_jsonz, - keys_jsonz_size, - &ehash); - end = GNUNET_STRINGS_data_to_string (&ehash, - sizeof (ehash), - etag, - sizeof (etag)); - *end = '\0'; - } - - /* Create uncompressed response */ - krd.response_uncompressed - = MHD_create_response_from_buffer (keys_jsonz_size, - keys_json, - MHD_RESPMEM_MUST_FREE); - GNUNET_assert (NULL != krd.response_uncompressed); - GNUNET_assert (GNUNET_OK == - setup_general_response_headers (ksh, - wsh, - krd.response_uncompressed)); - GNUNET_break (MHD_YES == - MHD_add_response_header (krd.response_uncompressed, - MHD_HTTP_HEADER_ETAG, - etag)); - /* Also compute compressed version of /keys response */ - comp = TALER_MHD_body_compress (&keys_jsonz, - &keys_jsonz_size); - krd.response_compressed - = MHD_create_response_from_buffer (keys_jsonz_size, - keys_jsonz, - MHD_RESPMEM_MUST_FREE); - GNUNET_assert (NULL != krd.response_compressed); - /* If the response is actually compressed, set the - respective header. */ - GNUNET_assert ( (MHD_YES != comp) || - (MHD_YES == - MHD_add_response_header (krd.response_compressed, - MHD_HTTP_HEADER_CONTENT_ENCODING, - "deflate")) ); - GNUNET_assert (GNUNET_OK == - setup_general_response_headers (ksh, - wsh, - krd.response_compressed)); - /* Set cache control headers: our response varies depending on these headers */ - GNUNET_break (MHD_YES == - MHD_add_response_header (wsh->wire_reply, - MHD_HTTP_HEADER_VARY, - MHD_HTTP_HEADER_ACCEPT_ENCODING)); - /* Information is always public, revalidate after 1 day */ - GNUNET_break (MHD_YES == - MHD_add_response_header (wsh->wire_reply, - MHD_HTTP_HEADER_CACHE_CONTROL, - "public,max-age=86400")); - GNUNET_break (MHD_YES == - MHD_add_response_header (krd.response_compressed, - MHD_HTTP_HEADER_ETAG, - etag)); - krd.etag = GNUNET_strdup (etag); - } - krd.cherry_pick_date = last_cherry_pick_date; - GNUNET_array_append (ksh->krd_array, - ksh->krd_array_length, - krd); - return GNUNET_OK; -} - - -/** - * Update the "/keys" responses in @a ksh, computing the detailed replies. - * - * This function is to recompute all (including cherry-picked) responses we - * might want to return, based on the state already in @a ksh. - * - * @param[in,out] ksh state handle to update - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -finish_keys_response (struct DH_KeyStateHandle *ksh) -{ - enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR; - json_t *recoup; - struct SignKeyCtx sctx; - json_t *grouped_denominations = NULL; - struct GNUNET_TIME_Timestamp last_cherry_pick_date; - struct GNUNET_CONTAINER_Heap *heap; - struct GNUNET_HashContext *hash_context = NULL; - struct GNUNET_HashCode grouped_hash_xor = {0}; - /* Remember if we have any denomination with age restriction */ - bool has_age_restricted_denomination = false; - - sctx.signkeys = json_array (); - GNUNET_assert (NULL != sctx.signkeys); - sctx.min_sk_frequency = GNUNET_TIME_UNIT_FOREVER_REL; - GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map, - &add_sign_key_cb, - &sctx); - recoup = json_array (); - GNUNET_assert (NULL != recoup); - heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX); - { - struct DenomKeyCtx dkc = { - .recoup = recoup, - .heap = heap, - .min_dk_frequency = GNUNET_TIME_UNIT_FOREVER_REL, - }; - - GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map, - &add_denom_key_cb, - &dkc); - ksh->rekey_frequency - = GNUNET_TIME_relative_min (dkc.min_dk_frequency, - sctx.min_sk_frequency); - } - - hash_context = GNUNET_CRYPTO_hash_context_start (); - - grouped_denominations = json_array (); - GNUNET_assert (NULL != grouped_denominations); - - last_cherry_pick_date = GNUNET_TIME_UNIT_ZERO_TS; - - // FIXME: This block contains the implementation of the DEPRECATED - // "denom_pubs" array along with the new grouped "denominations". - // "denom_pubs" Will be removed sooner or later. - { - struct DH_DonationUnitKey *dk; - struct GNUNET_CONTAINER_MultiHashMap *denominations_by_group; - /* GroupData is the value we store for each group meta-data */ - struct GroupData - { - /** - * The json blob with the group meta-data and list of denominations - */ - json_t *json; - - /** - * xor of all hashes of denominations in that group - */ - struct GNUNET_HashCode hash_xor; - }; - - denominations_by_group = - GNUNET_CONTAINER_multihashmap_create (1024, - GNUNET_NO /* NO, because keys are only on the stack */ - ); - - - /* heap = min heap, sorted by start time */ - while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap))) - { - if (GNUNET_TIME_timestamp_cmp (last_cherry_pick_date, - !=, - dk->meta.start) && - (! GNUNET_TIME_absolute_is_zero (last_cherry_pick_date.abs_time)) ) - { - /* - * This is not the first entry in the heap (because last_cherry_pick_date != - * GNUNET_TIME_UNIT_ZERO_TS) and the previous entry had a different - * start time. Therefore, we create a new entry in ksh. - */ - struct GNUNET_HashCode hc; - - GNUNET_CRYPTO_hash_context_finish ( - GNUNET_CRYPTO_hash_context_copy (hash_context), - &hc); - - if (GNUNET_OK != - create_krd (ksh, - &hc, - last_cherry_pick_date, - sctx.signkeys, - recoup, - grouped_denominations, - &grouped_hash_xor)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to generate key response data for %s\n", - GNUNET_TIME_timestamp2s (last_cherry_pick_date)); - GNUNET_CRYPTO_hash_context_abort (hash_context); - /* drain heap before destroying it */ - while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap))) - /* intentionally empty */; - GNUNET_CONTAINER_heap_destroy (heap); - goto CLEANUP; - } - } - - last_cherry_pick_date = dk->meta.start; - /* - * Group the denominations by {cipher, value}. - * - * For each group we save the group meta-data and the list of - * denominations in this group as a json-blob in the multihashmap - * denominations_by_group. - */ - { - static const char *denoms_key = "denoms"; - struct GroupData *group; - json_t *list; - json_t *entry; - struct GNUNET_HashCode key; - struct TALER_DenominationGroup meta = { - .cipher = dk->denom_pub.cipher, - .value = dk->meta.value - }; - - /* Search the group/JSON-blob for the key */ - TALER_denomination_group_get_key (&meta, - &key); - group = GNUNET_CONTAINER_multihashmap_get ( - denominations_by_group, - &key); - if (NULL == group) - { - /* There is no group for this meta-data yet, so we create a new group */ - bool age_restricted = meta.age_mask.bits != 0; - const char *cipher; - - group = GNUNET_new (struct GroupData); - switch (meta.cipher) - { - case TALER_DENOMINATION_RSA: - cipher = age_restricted ? "RSA+age_restricted" : "RSA"; - break; - case TALER_DENOMINATION_CS: - cipher = age_restricted ? "CS+age_restricted" : "CS"; - break; - default: - GNUNET_assert (false); - } - - group->json = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("cipher", - cipher), - TALER_JSON_pack_amount ("value", - &meta.value)); - GNUNET_assert (NULL != group->json); - - if (age_restricted) - { - int r = json_object_set_new (group->json, - "age_mask", - json_integer (meta.age_mask.bits)); - - GNUNET_assert (0 == r); - - /* Remember that we have found at least _one_ age restricted denomination */ - has_age_restricted_denomination = true; - } - - /* Create a new array for the denominations in this group */ - list = json_array (); - GNUNET_assert (NULL != list); - GNUNET_assert (0 == - json_object_set_new (group->json, - denoms_key, - list)); - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multihashmap_put (denominations_by_group, - &key, - group, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - } - - /* Now that we have found/created the right group, add the - denomination to the list */ - { - struct HelperDonationUnit *hd; - struct GNUNET_JSON_PackSpec key_spec; - bool private_key_lost; - - hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys, - &dk->h_denom_pub.hash); - private_key_lost - = (NULL == hd) || - GNUNET_TIME_absolute_is_past ( - GNUNET_TIME_absolute_add ( - hd->start_time.abs_time, - hd->validity_duration)); - switch (meta.cipher) - { - case TALER_DENOMINATION_RSA: - key_spec = - GNUNET_JSON_pack_rsa_public_key ( - "rsa_pub", - dk->denom_pub.details.rsa_public_key); - break; - case TALER_DENOMINATION_CS: - key_spec = - GNUNET_JSON_pack_data_varsize ( - "cs_pub", - &dk->denom_pub.details.cs_public_key, - sizeof (dk->denom_pub.details.cs_public_key)); - break; - default: - GNUNET_assert (false); - } - - entry = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("master_sig", - &dk->master_sig), - GNUNET_JSON_pack_allow_null ( - private_key_lost - ? GNUNET_JSON_pack_bool ("lost", - true) - : GNUNET_JSON_pack_string ("dummy", - NULL)), - GNUNET_JSON_pack_timestamp ("stamp_start", - dk->meta.start), - GNUNET_JSON_pack_timestamp ("stamp_expire_withdraw", - dk->meta.expire_withdraw), - GNUNET_JSON_pack_timestamp ("stamp_expire_deposit", - dk->meta.expire_deposit), - GNUNET_JSON_pack_timestamp ("stamp_expire_legal", - dk->meta.expire_legal), - key_spec - ); - GNUNET_assert (NULL != entry); - } - - /* Build up the running xor of all hashes of the denominations in this - group */ - GNUNET_CRYPTO_hash_xor (&dk->h_denom_pub.hash, - &group->hash_xor, - &group->hash_xor); - - /* Finally, add the denomination to the list of denominations in this - group */ - list = json_object_get (group->json, denoms_key); - GNUNET_assert (NULL != list); - GNUNET_assert (true == json_is_array (list)); - GNUNET_assert (0 == - json_array_append_new (list, entry)); - } - } /* loop over heap ends */ - - /* Create the JSON-array of grouped denominations */ - if (0 < - GNUNET_CONTAINER_multihashmap_size (denominations_by_group)) - { - struct GNUNET_CONTAINER_MultiHashMapIterator *iter; - struct GroupData *group = NULL; - - iter = - GNUNET_CONTAINER_multihashmap_iterator_create (denominations_by_group); - - while (GNUNET_OK == - GNUNET_CONTAINER_multihashmap_iterator_next (iter, - NULL, - (const - void **) &group)) - { - /* Add the XOR over all hashes of denominations in this group to the group */ - GNUNET_assert (0 == - json_object_set_new ( - group->json, - "hash", - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto (NULL, - &group->hash_xor)))); - - /* Add this group to the array */ - GNUNET_assert (0 == - json_array_append_new ( - grouped_denominations, - group->json)); - /* Build the running XOR over all hash(_xor) */ - GNUNET_CRYPTO_hash_xor (&group->hash_xor, - &grouped_hash_xor, - &grouped_hash_xor); - GNUNET_free (group); - } - GNUNET_CONTAINER_multihashmap_iterator_destroy (iter); - - } - - GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group); - } - - GNUNET_CONTAINER_heap_destroy (heap); - if (! GNUNET_TIME_absolute_is_zero (last_cherry_pick_date.abs_time)) - { - struct GNUNET_HashCode hc; - - GNUNET_CRYPTO_hash_context_finish (hash_context, - &hc); - if (GNUNET_OK != - create_krd (ksh, - &hc, - last_cherry_pick_date, - sctx.signkeys, - recoup, - grouped_denominations, - &grouped_hash_xor)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to generate key response data for %s\n", - GNUNET_TIME_timestamp2s (last_cherry_pick_date)); - goto CLEANUP; - } - ksh->management_only = false; - - /* Sanity check: Make sure that age restriction is enabled IFF at least - * one age restricted denomination exist */ - if (! has_age_restricted_denomination && DH_age_restriction_enabled) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Age restriction is enabled, but NO denominations with age restriction found!\n"); - goto CLEANUP; - } - else if (has_age_restricted_denomination && ! DH_age_restriction_enabled) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Age restriction is NOT enabled, but denominations with age restriction found!\n"); - goto CLEANUP; - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "No denomination keys available. Refusing to generate /keys response.\n"); - GNUNET_CRYPTO_hash_context_abort (hash_context); - } - - ret = GNUNET_OK; - -CLEANUP: - json_decref (grouped_denominations); - json_decref (sctx.signkeys); - json_decref (recoup); - return ret; -} - - -/** - * Create a key state. - * - * @param[in] hs helper state to (re)use, NULL if not available - * @param management_only if we should NOT run 'finish_keys_response()' - * because we only need the state for the /management/keys API - * @return NULL on error (i.e. failed to access database) - */ -static struct DH_KeyStateHandle * -build_key_state (struct HelperState *hs, - bool management_only) -{ - struct DH_KeyStateHandle *ksh; - enum GNUNET_DB_QueryStatus qs; - - ksh = GNUNET_new (struct DH_KeyStateHandle); - ksh->signature_expires = GNUNET_TIME_UNIT_FOREVER_TS; - ksh->reload_time = GNUNET_TIME_timestamp_get (); - /* We must use the key_generation from when we STARTED the process! */ - ksh->key_generation = key_generation; - if (NULL == hs) - { - ksh->helpers = GNUNET_new (struct HelperState); - if (GNUNET_OK != - setup_key_helpers (ksh->helpers)) - { - GNUNET_free (ksh->helpers); - GNUNET_assert (NULL == ksh->management_keys_reply); - GNUNET_free (ksh); - return NULL; - } - } - else - { - ksh->helpers = hs; - } - ksh->denomkey_map = GNUNET_CONTAINER_multihashmap_create (1024, - true); - ksh->signkey_map = GNUNET_CONTAINER_multipeermap_create (32, - false /* MUST be false! */ - ); - /* NOTE: fetches master-signed signkeys, but ALSO those that were revoked! */ - GNUNET_break (GNUNET_OK == - DH_plugin->preflight (DH_plugin->cls)); - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); - destroy_key_state (ksh, - true); - return NULL; - } - qs = DH_plugin->iterate_denominations (DH_plugin->cls, - &denomination_info_cb, - ksh); - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); - destroy_key_state (ksh, - true); - return NULL; - } - /* NOTE: ONLY fetches non-revoked AND master-signed signkeys! */ - qs = DH_plugin->iterate_active_signkeys (DH_plugin->cls, - &signkey_info_cb, - ksh); - if (qs < 0) - { - GNUNET_break (0); - destroy_key_state (ksh, - true); - return NULL; - } - qs = DH_plugin->iterate_auditor_denominations (DH_plugin->cls, - &auditor_denom_cb, - ksh); - if (qs < 0) - { - GNUNET_break (0); - destroy_key_state (ksh, - true); - return NULL; - } - qs = DH_plugin->iterate_active_auditors (DH_plugin->cls, - &auditor_info_cb, - ksh); - if (qs < 0) - { - GNUNET_break (0); - destroy_key_state (ksh, - true); - return NULL; - } - - if (management_only) - { - ksh->management_only = true; - return ksh; - } - - if (GNUNET_OK != - finish_keys_response (ksh)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Could not finish /keys response (likely no signing keys available yet)\n"); - destroy_key_state (ksh, - true); - return NULL; - } - - return ksh; -} - - -void -DH_keys_update_states () -{ - struct GNUNET_DB_EventHeaderP es = { - .size = htons (sizeof (es)), - .type = htons (TALER_DBEVENT_DONAU_KEYS_UPDATED), - }; - - DH_plugin->event_notify (DH_plugin->cls, - &es, - NULL, - 0); - key_generation++; - DH_resume_keys_requests (false); -} - - -static struct DH_KeyStateHandle * -keys_get_state (bool management_only) -{ - struct DH_KeyStateHandle *old_ksh; - struct DH_KeyStateHandle *ksh; - - old_ksh = key_state; - if (NULL == old_ksh) - { - ksh = build_key_state (NULL, - management_only); - if (NULL == ksh) - return NULL; - key_state = ksh; - return ksh; - } - if ( (old_ksh->key_generation < key_generation) || - (GNUNET_TIME_absolute_is_past (old_ksh->signature_expires.abs_time)) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Rebuilding /keys, generation upgrade from %llu to %llu\n", - (unsigned long long) old_ksh->key_generation, - (unsigned long long) key_generation); - ksh = build_key_state (old_ksh->helpers, - management_only); - key_state = ksh; - old_ksh->helpers = NULL; - destroy_key_state (old_ksh, - false); - return ksh; - } - sync_key_helpers (old_ksh->helpers); - return old_ksh; -} - - -struct DH_KeyStateHandle * -DH_keys_get_state_for_management_only (void) -{ - return keys_get_state (true); -} - - -struct DH_KeyStateHandle * -DH_keys_get_state (void) -{ - struct DH_KeyStateHandle *ksh; - - ksh = keys_get_state (false); - if (NULL == ksh) - return NULL; - - if (ksh->management_only) - { - if (GNUNET_OK != - finish_keys_response (ksh)) - return NULL; - } - - return ksh; -} struct DH_DonationUnitKey * @@ -2497,45 +1362,6 @@ DH_keys_denomination_cs_batch_r_pub ( r_pubs); } - -void -DH_keys_denomination_revoke (const struct TALER_DenominationHashP *h_denom_pub) -{ - struct DH_KeyStateHandle *ksh; - struct HelperDonationUnit *hd; - - ksh = DH_keys_get_state (); - if (NULL == ksh) - { - GNUNET_break (0); - return; - } - hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys, - &h_denom_pub->hash); - if (NULL == hd) - { - GNUNET_break (0); - return; - } - switch (hd->denom_pub.cipher) - { - case TALER_DENOMINATION_RSA: - TALER_CRYPTO_helper_rsa_revoke (ksh->helpers->rsadh, - &hd->h_details.h_rsa); - DH_keys_update_states (); - return; - case TALER_DENOMINATION_CS: - TALER_CRYPTO_helper_cs_revoke (ksh->helpers->csdh, - &hd->h_details.h_cs); - DH_keys_update_states (); - return; - default: - GNUNET_break (0); - return; - } -} - - enum TALER_ErrorCode DH_keys_donau_sign_ ( const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, @@ -2559,95 +1385,6 @@ DH_keys_donau_sign_ ( sig); } - -enum TALER_ErrorCode -DH_keys_donau_sign2_ ( - void *cls, - const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, - struct DONAU_DonauPublicKeyP *pub, - struct TALER_DonauSignatureP *sig) -{ - struct DH_KeyStateHandle *ksh = cls; - enum TALER_ErrorCode ec; - - DH_METRICS_num_signatures[DH_MT_SIGNATURE_EDDSA]++; - ec = TALER_CRYPTO_helper_esign_sign_ (ksh->helpers->esh, - purpose, - pub, - sig); - if (TALER_EC_NONE != ec) - return ec; - { - /* Here we check here that 'pub' is set to an donau public key that is - actually signed by the master key! Otherwise, we happily continue to - use key material even if the offline signatures have not been made - yet! */ - struct GNUNET_PeerIdentity pid; - struct SigningKey *sk; - - pid.public_key = pub->eddsa_pub; - sk = GNUNET_CONTAINER_multipeermap_get (ksh->signkey_map, - &pid); - if (NULL == sk) - { - /* just to be safe, zero out the (valid) signature, as the key - should not or no longer be used */ - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Cannot sign, offline key signatures are missing!\n"); - memset (sig, - 0, - sizeof (*sig)); - return TALER_EC_DONAU_SIGNKEY_HELPER_BUG; - } - } - return ec; -} - - -void -DH_keys_donau_revoke (const struct DONAU_DonauPublicKeyP *donau_pub) -{ - struct DH_KeyStateHandle *ksh; - - ksh = DH_keys_get_state (); - if (NULL == ksh) - { - GNUNET_break (0); - return; - } - TALER_CRYPTO_helper_esign_revoke (ksh->helpers->esh, - donau_pub); - DH_keys_update_states (); -} - - -/** - * Comparator used for a binary search by cherry_pick_date for @a key in the - * `struct KeysResponseData` array. See libc's qsort() and bsearch() functions. - * - * @param key pointer to a `struct GNUNET_TIME_Timestamp` - * @param value pointer to a `struct KeysResponseData` array entry - * @return 0 if time matches, -1 if key is smaller, 1 if key is larger - */ -static int -krd_search_comparator (const void *key, - const void *value) -{ - const struct GNUNET_TIME_Timestamp *kd = key; - const struct KeysResponseData *krd = value; - - if (GNUNET_TIME_timestamp_cmp (*kd, - >, - krd->cherry_pick_date)) - return -1; - if (GNUNET_TIME_timestamp_cmp (*kd, - <, - krd->cherry_pick_date)) - return 1; - return 0; -} - - MHD_RESULT DH_keys_get_handler (struct DH_RequestContext *rc, const char *const args[]) @@ -2762,217 +1499,4 @@ DH_keys_get_handler (struct DH_RequestContext *rc, } -/** - * Closure for #add_future_denomkey_cb and #add_future_signkey_cb. - */ -struct FutureBuilderContext -{ - /** - * Our key state. - */ - struct DH_KeyStateHandle *ksh; - - /** - * Array of denomination keys. - */ - json_t *denoms; - - /** - * Array of signing keys. - */ - json_t *signkeys; - -}; - - -/** - * Function called on all of our current and future denomination keys - * known to the helper process. Filters out those that are current - * and adds the remaining denomination keys (with their configuration - * data) to the JSON array. - * - * @param cls the `struct FutureBuilderContext *` - * @param h_denom_pub hash of the denomination public key - * @param value a `struct HelperDonationUnit` - * @return #GNUNET_OK (continue to iterate) - */ -static enum GNUNET_GenericReturnValue -add_future_denomkey_cb (void *cls, - const struct GNUNET_HashCode *h_denom_pub, - void *value) -{ - struct FutureBuilderContext *fbc = cls; - struct HelperDonationUnit *hd = value; - struct DH_DonationUnitKey *dk; - struct DONAUDB_DenominationKeyMetaData meta = {0}; - - dk = GNUNET_CONTAINER_multihashmap_get (fbc->ksh->denomkey_map, - h_denom_pub); - if (NULL != dk) - return GNUNET_OK; /* skip: this key is already active! */ - if (GNUNET_TIME_relative_is_zero (hd->validity_duration)) - return GNUNET_OK; /* this key already expired! */ - meta.start = hd->start_time; - meta.expire_withdraw = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add (meta.start.abs_time, - hd->validity_duration)); - GNUNET_assert ( - 0 == - json_array_append_new ( - fbc->denoms, - GNUNET_JSON_PACK ( - TALER_JSON_pack_amount ("value", - &meta.value), - GNUNET_JSON_pack_timestamp ("stamp_start", - meta.start), - GNUNET_JSON_pack_timestamp ("stamp_expire_withdraw", - meta.expire_withdraw), - GNUNET_JSON_pack_timestamp ("stamp_expire_deposit", - meta.expire_deposit), - GNUNET_JSON_pack_timestamp ("stamp_expire_legal", - meta.expire_legal), - TALER_JSON_pack_denom_pub ("denom_pub", - &hd->denom_pub), - GNUNET_JSON_pack_data_auto ("denom_secmod_sig", - &hd->sm_sig), - GNUNET_JSON_pack_string ("section_name", - hd->section_name)))); - return GNUNET_OK; -} - - -/** - * Function called on all of our current and future donau signing keys - * known to the helper process. Filters out those that are current - * and adds the remaining signing keys (with their configuration - * data) to the JSON array. - * - * @param cls the `struct FutureBuilderContext *` - * @param pid actually the donau public key (type disguised) - * @param value a `struct HelperDonationUnit` - * @return #GNUNET_OK (continue to iterate) - */ -static enum GNUNET_GenericReturnValue -add_future_signkey_cb (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct FutureBuilderContext *fbc = cls; - struct HelperSignkey *hsk = value; - struct SigningKey *sk; - struct GNUNET_TIME_Timestamp stamp_expire; - struct GNUNET_TIME_Timestamp legal_end; - - sk = GNUNET_CONTAINER_multipeermap_get (fbc->ksh->signkey_map, - pid); - if (NULL != sk) - return GNUNET_OK; /* skip: this key is already active */ - if (GNUNET_TIME_relative_is_zero (hsk->validity_duration)) - return GNUNET_OK; /* this key already expired! */ - stamp_expire = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add (hsk->start_time.abs_time, - hsk->validity_duration)); - legal_end = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add (stamp_expire.abs_time, - signkey_legal_duration)); - GNUNET_assert (0 == - json_array_append_new ( - fbc->signkeys, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("key", - &hsk->donau_pub), - GNUNET_JSON_pack_timestamp ("stamp_start", - hsk->start_time), - GNUNET_JSON_pack_timestamp ("stamp_expire", - stamp_expire), - GNUNET_JSON_pack_timestamp ("stamp_end", - legal_end), - GNUNET_JSON_pack_data_auto ("signkey_secmod_sig", - &hsk->sm_sig)))); - return GNUNET_OK; -} - - -MHD_RESULT -DH_keys_management_get_keys_handler (const struct DH_RequestHandler *rh, - struct MHD_Connection *connection) -{ - struct DH_KeyStateHandle *ksh; - json_t *reply; - - (void) rh; - ksh = DH_keys_get_state_for_management_only (); - if (NULL == ksh) - { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_SERVICE_UNAVAILABLE, - TALER_EC_DONAU_GENERIC_KEYS_MISSING, - "no key state"); - } - sync_key_helpers (ksh->helpers); - if (NULL == ksh->management_keys_reply) - { - struct FutureBuilderContext fbc = { - .ksh = ksh, - .denoms = json_array (), - .signkeys = json_array () - }; - - if ( (GNUNET_is_zero (&denom_rsa_sm_pub)) && - (GNUNET_is_zero (&denom_cs_sm_pub)) ) - { - /* Either IPC failed, or neither helper had any denominations configured. */ - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_GATEWAY, - TALER_EC_DONAU_DENOMINATION_HELPER_UNAVAILABLE, - NULL); - } - if (GNUNET_is_zero (&esign_sm_pub)) - { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_GATEWAY, - TALER_EC_DONAU_SIGNKEY_HELPER_UNAVAILABLE, - NULL); - } - GNUNET_assert (NULL != fbc.denoms); - GNUNET_assert (NULL != fbc.signkeys); - GNUNET_CONTAINER_multihashmap_iterate (ksh->helpers->denom_keys, - &add_future_denomkey_cb, - &fbc); - GNUNET_CONTAINER_multipeermap_iterate (ksh->helpers->esign_keys, - &add_future_signkey_cb, - &fbc); - reply = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("future_denoms", - fbc.denoms), - GNUNET_JSON_pack_array_steal ("future_signkeys", - fbc.signkeys), - GNUNET_JSON_pack_data_auto ("master_pub", - &DH_master_public_key), - GNUNET_JSON_pack_data_auto ("denom_secmod_public_key", - &denom_rsa_sm_pub), - GNUNET_JSON_pack_data_auto ("denom_secmod_cs_public_key", - &denom_cs_sm_pub), - GNUNET_JSON_pack_data_auto ("signkey_secmod_public_key", - &esign_sm_pub)); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Returning GET /management/keys response:\n"); - if (NULL == reply) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE, - NULL); - GNUNET_assert (NULL == ksh->management_keys_reply); - ksh->management_keys_reply = reply; - } - else - { - reply = ksh->management_keys_reply; - } - return TALER_MHD_reply_json (connection, - reply, - MHD_HTTP_OK); -} - - /* end of donau-httpd_keys.c */ diff --git a/src/donau/donau-httpd_responses.c b/src/donau/donau-httpd_responses.c @@ -1,560 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-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 CHARITYABILITY 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-donau-httpd_responses.c - * @brief API for generating generic replies of the donau; these - * functions are called DH_RESPONSE_reply_ and they generate - * and queue MHD response objects for a given connection. - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <microhttpd.h> -#include <zlib.h> -#include "donau-httpd_responses.h" -#include <taler/taler_util.h> -#include "taler/taler_json_lib.h" -#include "taler/taler_mhd_lib.h" -#include "donau-httpd_keys.h" - - -// /** -// * Compile the transaction history of a coin into a JSON object. -// * -// * @param coin_pub public key of the coin -// * @param tl transaction history to JSON-ify -// * @return json representation of the @a rh, NULL on error -// */ -// json_t * -// DH_RESPONSE_compile_transaction_history ( -// const struct TALER_CoinSpendPublicKeyP *coin_pub, -// const struct DONAUDB_TransactionList *tl) -// { -// json_t *history; - -// history = json_array (); -// if (NULL == history) -// { -// GNUNET_break (0); /* out of memory!? */ -// return NULL; -// } -// for (const struct DONAUDB_TransactionList *pos = tl; -// NULL != pos; -// pos = pos->next) -// { -// switch (pos->type) -// { -// case DONAUDB_TT_DEPOSIT: -// { -// const struct DONAUDB_DepositListEntry *deposit = -// pos->details.deposit; -// struct TALER_CharityWireHashP h_wire; - -// TALER_charity_wire_signature_hash (deposit->receiver_wire_account, -// &deposit->wire_salt, -// &h_wire); -// #if ENABLE_SANITY_CHECKS -// /* internal sanity check before we hand out a bogus sig... */ -// DH_METRICS_num_verifications[DH_MT_SIGNATURE_EDDSA]++; -// if (GNUNET_OK != -// TALER_wallet_deposit_verify ( -// &deposit->amount_with_fee, -// &deposit->deposit_fee, -// &h_wire, -// &deposit->h_contract_terms, -// deposit->no_wallet_data_hash -// ? NULL -// : &deposit->wallet_data_hash, -// deposit->no_age_commitment -// ? NULL -// : &deposit->h_age_commitment, -// &deposit->h_policy, -// &deposit->h_denom_pub, -// deposit->timestamp, -// &deposit->charity_pub, -// deposit->refund_deadline, -// coin_pub, -// &deposit->csig)) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// #endif -// if (0 != -// json_array_append_new ( -// history, -// GNUNET_JSON_PACK ( -// GNUNET_JSON_pack_string ("type", -// "DEPOSIT"), -// TALER_JSON_pack_amount ("amount", -// &deposit->amount_with_fee), -// TALER_JSON_pack_amount ("deposit_fee", -// &deposit->deposit_fee), -// GNUNET_JSON_pack_timestamp ("timestamp", -// deposit->timestamp), -// GNUNET_JSON_pack_allow_null ( -// GNUNET_JSON_pack_timestamp ("refund_deadline", -// deposit->refund_deadline)), -// GNUNET_JSON_pack_data_auto ("charity_pub", -// &deposit->charity_pub), -// GNUNET_JSON_pack_data_auto ("h_contract_terms", -// &deposit->h_contract_terms), -// GNUNET_JSON_pack_data_auto ("h_wire", -// &h_wire), -// GNUNET_JSON_pack_allow_null ( -// deposit->no_age_commitment ? -// GNUNET_JSON_pack_string ( -// "h_age_commitment", NULL) : -// GNUNET_JSON_pack_data_auto ("h_age_commitment", -// &deposit->h_age_commitment)), -// GNUNET_JSON_pack_data_auto ("coin_sig", -// &deposit->csig)))) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// break; -// } -// case DONAUDB_TT_MELT: -// { -// const struct DONAUDB_MeltListEntry *melt = -// pos->details.melt; -// const struct TALER_AgeCommitmentHash *phac = NULL; - -// #if ENABLE_SANITY_CHECKS -// DH_METRICS_num_verifications[DH_MT_SIGNATURE_EDDSA]++; -// if (GNUNET_OK != -// TALER_wallet_melt_verify ( -// &melt->amount_with_fee, -// &melt->melt_fee, -// &melt->rc, -// &melt->h_denom_pub, -// &melt->h_age_commitment, -// coin_pub, -// &melt->coin_sig)) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// #endif - -// /* Age restriction is optional. We communicate a NULL value to -// * JSON_PACK below */ -// if (! melt->no_age_commitment) -// phac = &melt->h_age_commitment; - -// if (0 != -// json_array_append_new ( -// history, -// GNUNET_JSON_PACK ( -// GNUNET_JSON_pack_string ("type", -// "MELT"), -// TALER_JSON_pack_amount ("amount", -// &melt->amount_with_fee), -// TALER_JSON_pack_amount ("melt_fee", -// &melt->melt_fee), -// GNUNET_JSON_pack_data_auto ("rc", -// &melt->rc), -// GNUNET_JSON_pack_allow_null ( -// GNUNET_JSON_pack_data_auto ("h_age_commitment", -// phac)), -// GNUNET_JSON_pack_data_auto ("coin_sig", -// &melt->coin_sig)))) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// } -// break; -// case DONAUDB_TT_REFUND: -// { -// const struct DONAUDB_RefundListEntry *refund = -// pos->details.refund; -// struct TALER_Amount value; - -// #if ENABLE_SANITY_CHECKS -// DH_METRICS_num_verifications[DH_MT_SIGNATURE_EDDSA]++; -// if (GNUNET_OK != -// TALER_charity_refund_verify ( -// coin_pub, -// &refund->h_contract_terms, -// refund->rtransaction_id, -// &refund->refund_amount, -// &refund->charity_pub, -// &refund->charity_sig)) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// #endif -// if (0 > -// TALER_amount_subtract (&value, -// &refund->refund_amount, -// &refund->refund_fee)) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// if (0 != -// json_array_append_new ( -// history, -// GNUNET_JSON_PACK ( -// GNUNET_JSON_pack_string ("type", -// "REFUND"), -// TALER_JSON_pack_amount ("amount", -// &value), -// TALER_JSON_pack_amount ("refund_fee", -// &refund->refund_fee), -// GNUNET_JSON_pack_data_auto ("h_contract_terms", -// &refund->h_contract_terms), -// GNUNET_JSON_pack_data_auto ("charity_pub", -// &refund->charity_pub), -// GNUNET_JSON_pack_uint64 ("rtransaction_id", -// refund->rtransaction_id), -// GNUNET_JSON_pack_data_auto ("charity_sig", -// &refund->charity_sig)))) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// } -// break; -// case DONAUDB_TT_OLD_COIN_RECOUP: -// { -// struct DONAUDB_RecoupRefreshListEntry *pr = -// pos->details.old_coin_recoup; -// struct DONAU_DonauPublicKeyP epub; -// struct TALER_DonauSignatureP esig; - -// if (TALER_EC_NONE != -// TALER_donau_online_confirm_recoup_refresh_sign ( -// &DH_keys_donau_sign_, -// pr->timestamp, -// &pr->value, -// &pr->coin.coin_pub, -// &pr->old_coin_pub, -// &epub, -// &esig)) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// /* NOTE: we could also provide coin_pub's coin_sig, denomination key hash and -// the denomination key's RSA signature over coin_pub, but as the -// wallet should really already have this information (and cannot -// check or do anything with it anyway if it doesn't), it seems -// strictly unnecessary. */ -// if (0 != -// json_array_append_new ( -// history, -// GNUNET_JSON_PACK ( -// GNUNET_JSON_pack_string ("type", -// "OLD-COIN-RECOUP"), -// TALER_JSON_pack_amount ("amount", -// &pr->value), -// GNUNET_JSON_pack_data_auto ("donau_sig", -// &esig), -// GNUNET_JSON_pack_data_auto ("donau_pub", -// &epub), -// GNUNET_JSON_pack_data_auto ("coin_pub", -// &pr->coin.coin_pub), -// GNUNET_JSON_pack_timestamp ("timestamp", -// pr->timestamp)))) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// break; -// } -// case DONAUDB_TT_RECOUP: -// { -// const struct DONAUDB_RecoupListEntry *recoup = -// pos->details.recoup; -// struct DONAU_DonauPublicKeyP epub; -// struct TALER_DonauSignatureP esig; - -// if (TALER_EC_NONE != -// TALER_donau_online_confirm_recoup_sign ( -// &DH_keys_donau_sign_, -// recoup->timestamp, -// &recoup->value, -// coin_pub, -// &recoup->reserve_pub, -// &epub, -// &esig)) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// if (0 != -// json_array_append_new ( -// history, -// GNUNET_JSON_PACK ( -// GNUNET_JSON_pack_string ("type", -// "RECOUP"), -// TALER_JSON_pack_amount ("amount", -// &recoup->value), -// GNUNET_JSON_pack_data_auto ("donau_sig", -// &esig), -// GNUNET_JSON_pack_data_auto ("donau_pub", -// &epub), -// GNUNET_JSON_pack_data_auto ("reserve_pub", -// &recoup->reserve_pub), -// GNUNET_JSON_pack_data_auto ("coin_sig", -// &recoup->coin_sig), -// GNUNET_JSON_pack_data_auto ("coin_blind", -// &recoup->coin_blind), -// GNUNET_JSON_pack_data_auto ("reserve_pub", -// &recoup->reserve_pub), -// GNUNET_JSON_pack_timestamp ("timestamp", -// recoup->timestamp)))) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// } -// break; -// case DONAUDB_TT_RECOUP_REFRESH: -// { -// struct DONAUDB_RecoupRefreshListEntry *pr = -// pos->details.recoup_refresh; -// struct DONAU_DonauPublicKeyP epub; -// struct TALER_DonauSignatureP esig; - -// if (TALER_EC_NONE != -// TALER_donau_online_confirm_recoup_refresh_sign ( -// &DH_keys_donau_sign_, -// pr->timestamp, -// &pr->value, -// coin_pub, -// &pr->old_coin_pub, -// &epub, -// &esig)) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// /* NOTE: we could also provide coin_pub's coin_sig, denomination key -// hash and the denomination key's RSA signature over coin_pub, but as -// the wallet should really already have this information (and cannot -// check or do anything with it anyway if it doesn't), it seems -// strictly unnecessary. */ -// if (0 != -// json_array_append_new ( -// history, -// GNUNET_JSON_PACK ( -// GNUNET_JSON_pack_string ("type", -// "RECOUP-REFRESH"), -// TALER_JSON_pack_amount ("amount", -// &pr->value), -// GNUNET_JSON_pack_data_auto ("donau_sig", -// &esig), -// GNUNET_JSON_pack_data_auto ("donau_pub", -// &epub), -// GNUNET_JSON_pack_data_auto ("old_coin_pub", -// &pr->old_coin_pub), -// GNUNET_JSON_pack_data_auto ("coin_sig", -// &pr->coin_sig), -// GNUNET_JSON_pack_data_auto ("coin_blind", -// &pr->coin_blind), -// GNUNET_JSON_pack_timestamp ("timestamp", -// pr->timestamp)))) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// break; -// } - -// case DONAUDB_TT_PURSE_DEPOSIT: -// { -// struct DONAUDB_PurseDepositListEntry *pd -// = pos->details.purse_deposit; -// const struct TALER_AgeCommitmentHash *phac = NULL; - -// if (! pd->no_age_commitment) -// phac = &pd->h_age_commitment; - -// if (0 != -// json_array_append_new ( -// history, -// GNUNET_JSON_PACK ( -// GNUNET_JSON_pack_string ("type", -// "PURSE-DEPOSIT"), -// TALER_JSON_pack_amount ("amount", -// &pd->amount), -// GNUNET_JSON_pack_string ("donau_base_url", -// NULL == pd->donau_base_url -// ? DH_base_url -// : pd->donau_base_url), -// GNUNET_JSON_pack_allow_null ( -// GNUNET_JSON_pack_data_auto ("h_age_commitment", -// phac)), -// GNUNET_JSON_pack_data_auto ("purse_pub", -// &pd->purse_pub), -// GNUNET_JSON_pack_bool ("refunded", -// pd->refunded), -// GNUNET_JSON_pack_data_auto ("coin_sig", -// &pd->coin_sig)))) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// break; -// } - -// case DONAUDB_TT_PURSE_REFUND: -// { -// const struct DONAUDB_PurseRefundListEntry *prefund = -// pos->details.purse_refund; -// struct TALER_Amount value; -// enum TALER_ErrorCode ec; -// struct DONAU_DonauPublicKeyP epub; -// struct TALER_DonauSignatureP esig; - -// if (0 > -// TALER_amount_subtract (&value, -// &prefund->refund_amount, -// &prefund->refund_fee)) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// ec = TALER_donau_online_purse_refund_sign ( -// &DH_keys_donau_sign_, -// &value, -// &prefund->refund_fee, -// coin_pub, -// &prefund->purse_pub, -// &epub, -// &esig); -// if (TALER_EC_NONE != ec) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// if (0 != -// json_array_append_new ( -// history, -// GNUNET_JSON_PACK ( -// GNUNET_JSON_pack_string ("type", -// "PURSE-REFUND"), -// TALER_JSON_pack_amount ("amount", -// &value), -// TALER_JSON_pack_amount ("refund_fee", -// &prefund->refund_fee), -// GNUNET_JSON_pack_data_auto ("donau_sig", -// &esig), -// GNUNET_JSON_pack_data_auto ("donau_pub", -// &epub), -// GNUNET_JSON_pack_data_auto ("purse_pub", -// &prefund->purse_pub)))) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// } -// break; - -// case DONAUDB_TT_RESERVE_OPEN: -// { -// struct DONAUDB_ReserveOpenListEntry *role -// = pos->details.reserve_open; - -// if (0 != -// json_array_append_new ( -// history, -// GNUNET_JSON_PACK ( -// GNUNET_JSON_pack_string ("type", -// "RESERVE-OPEN-DEPOSIT"), -// TALER_JSON_pack_amount ("coin_contribution", -// &role->coin_contribution), -// GNUNET_JSON_pack_data_auto ("reserve_sig", -// &role->reserve_sig), -// GNUNET_JSON_pack_data_auto ("coin_sig", -// &role->coin_sig)))) -// { -// GNUNET_break (0); -// json_decref (history); -// return NULL; -// } -// break; -// } -// } -// } -// return history; -// } - - -// MHD_RESULT -// DH_RESPONSE_reply_unknown_denom_pub_hash ( -// struct MHD_Connection *connection, -// const struct TALER_DenominationHashP *dph) -// { -// struct DONAU_DonauPublicKeyP epub; -// struct TALER_DonauSignatureP esig; -// struct GNUNET_TIME_Timestamp now; -// enum TALER_ErrorCode ec; - -// now = GNUNET_TIME_timestamp_get (); -// ec = TALER_donau_online_denomination_unknown_sign ( -// &DH_keys_donau_sign_, -// now, -// dph, -// &epub, -// &esig); -// if (TALER_EC_NONE != ec) -// { -// GNUNET_break (0); -// return TALER_MHD_reply_with_error (connection, -// MHD_HTTP_INTERNAL_SERVER_ERROR, -// ec, -// NULL); -// } -// return TALER_MHD_REPLY_JSON_PACK ( -// connection, -// MHD_HTTP_NOT_FOUND, -// TALER_JSON_pack_ec (TALER_EC_DONAU_GENERIC_DENOMINATION_KEY_UNKNOWN), -// GNUNET_JSON_pack_timestamp ("timestamp", -// now), -// GNUNET_JSON_pack_data_auto ("donau_pub", -// &epub), -// GNUNET_JSON_pack_data_auto ("donau_sig", -// &esig), -// GNUNET_JSON_pack_data_auto ("h_denom_pub", -// dph)); -// } - - -/* end of taler-donau-httpd_responses.c */ diff --git a/src/donau/donau-httpd_responses.h b/src/donau/donau-httpd_responses.h @@ -1,201 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2022 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 CHARITYABILITY 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-donau-httpd_responses.h - * @brief API for generating generic replies of the donau; these - * functions are called DH_RESPONSE_reply_ and they generate - * and queue MHD response objects for a given connection. - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#ifndef DONAU_HTTPD_RESPONSES_H -#define DONAU_HTTPD_RESPONSES_H -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include "taler/taler_error_codes.h" -#include "donau-httpd.h" -#include "donau-httpd_db.h" -#include "donaudb_lib.h" -#include <gnunet/gnunet_mhd_compat.h> - - -/** - * Compile the history of a reserve into a JSON object. - * - * @param rh reserve history to JSON-ify - * @return json representation of the @a rh, NULL on error - */ -//json_t * -//DH_RESPONSE_compile_reserve_history ( -// const struct DONAUDB_ReserveHistory *rh); - - -/** - * Send assertion that the given denomination key hash - * is unknown to us at this time. - * - * @param connection connection to the client - * @param dph denomination public key hash - * @return MHD result code - */ -MHD_RESULT -DH_RESPONSE_reply_unknown_denom_pub_hash ( - struct MHD_Connection *connection, - const struct TALER_DenominationHashP *dph); - - -/** - * Return error message indicating that a reserve had - * an insufficient balance for the given operation. - * - * @param connection connection to the client - * @param ec specific error code to return with the reserve history - * @param balance_required the balance required for the operation - * @param reserve_pub the reserve with insufficient balance - * @return MHD result code - */ -MHD_RESULT -DH_RESPONSE_reply_reserve_insufficient_balance ( - struct MHD_Connection *connection, - enum TALER_ErrorCode ec, - const struct TALER_Amount *balance_required, - const struct TALER_ReservePublicKeyP *reserve_pub); - - -/** - * Send information that an AML process is blocking - * the operation right now. - * - * @param connection connection to the client - * @param status current AML status - * @return MHD result code - */ -MHD_RESULT -DH_RESPONSE_reply_aml_blocked (struct MHD_Connection *connection, - enum TALER_AmlDecisionState status); - - -/** - * Send assertion that the given denomination key hash - * is not usable (typically expired) at this time. - * - * @param connection connection to the client - * @param dph denomination public key hash - * @param ec error code to use - * @param oper name of the operation that is not allowed at this time - * @return MHD result code - */ -MHD_RESULT -DH_RESPONSE_reply_expired_denom_pub_hash ( - struct MHD_Connection *connection, - const struct TALER_DenominationHashP *dph, - enum TALER_ErrorCode ec, - const char *oper); - - -/** - * Send assertion that the given denomination cannot be used for this operation. - * - * @param connection connection to the client - * @param dph denomination public key hash - * @return MHD result code - */ -MHD_RESULT -DH_RESPONSE_reply_invalid_denom_cipher_for_operation ( - struct MHD_Connection *connection, - const struct TALER_DenominationHashP *dph); - - -/** - * Send proof that a request is invalid to client because of - * insufficient funds. This function will create a message with all - * of the operations affecting the coin that demonstrate that the coin - * has insufficient value. - * - * @param connection connection to the client - * @param ec error code to return - * @param h_denom_pub hash of the denomination of the coin - * @param coin_pub public key of the coin - * @return MHD result code - */ -MHD_RESULT -DH_RESPONSE_reply_coin_insufficient_funds ( - struct MHD_Connection *connection, - enum TALER_ErrorCode ec, - const struct TALER_DenominationHashP *h_denom_pub, - const struct TALER_CoinSpendPublicKeyP *coin_pub); - -/** - * Fundamental details about a purse. - */ -struct DH_PurseDetails -{ - /** - * When should the purse expire. - */ - struct GNUNET_TIME_Timestamp purse_expiration; - - /** - * Hash of the contract terms of the purse. - */ - struct TALER_PrivateContractHashP h_contract_terms; - - /** - * Public key of the purse we are creating. - */ - struct TALER_PurseContractPublicKeyP purse_pub; - - /** - * Total amount to be put into the purse. - */ - struct TALER_Amount target_amount; -}; - - -/** - * Send confirmation that a purse was created with - * the current purse balance. - * - * @param connection connection to the client - * @param pd purse details - * @param donau_timestamp our time for purse creation - * @param purse_balance current balance in the purse - * @return MHD result code - */ -MHD_RESULT -DH_RESPONSE_reply_purse_created ( - struct MHD_Connection *connection, - struct GNUNET_TIME_Timestamp donau_timestamp, - const struct TALER_Amount *purse_balance, - const struct DH_PurseDetails *pd); - - -/** - * Compile the transaction history of a coin into a JSON object. - * - * @param coin_pub public key of the coin - * @param tl transaction history to JSON-ify - * @return json representation of the @a rh, NULL on error - */ -json_t * -DH_RESPONSE_compile_transaction_history ( - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct DONAUDB_TransactionList *tl); - - -#endif diff --git a/src/donau/donau-httpd_submit_receipts_get.c b/src/donau/donau-httpd_submit_receipts_get.c @@ -1,519 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-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 CHARITYABILITY 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-donau-httpd_deposits_get.c - * @brief Handle wire deposit tracking-related requests - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include <pthread.h> -#include "taler_dbevents.h" -#include "taler/taler_json_lib.h" -#include "taler/taler_mhd_lib.h" -#include "taler_signatures.h" -#include "taler-donau-httpd_keys.h" -#include "taler-donau-httpd_deposits_get.h" -#include "taler-donau-httpd_responses.h" - - -/** - * Closure for #handle_wtid_data. - */ -struct DepositWtidContext -{ - - /** - * Kept in a DLL. - */ - struct DepositWtidContext *next; - - /** - * Kept in a DLL. - */ - struct DepositWtidContext *prev; - - /** - * Context for the request we are processing. - */ - struct DH_RequestContext *rc; - - /** - * Subscription for the database event we are waiting for. - */ - struct GNUNET_DB_EventHandler *eh; - - /** - * Hash over the proposal data of the contract for which this deposit is made. - */ - struct TALER_PrivateContractHashP h_contract_terms; - - /** - * Hash over the wiring information of the charity. - */ - struct TALER_CharityWireHashP h_wire; - - /** - * The Charity's public key. The deposit inquiry request is to be - * signed by the corresponding private key (using EdDSA). - */ - struct DONAU_CharityPublicKeyP charity; - - /** - * The coin's public key. This is the value that must have been - * signed (blindly) by the Donau. - */ - struct TALER_CoinSpendPublicKeyP coin_pub; - - /** - * Set by #handle_wtid data to the wire transfer ID. - */ - struct TALER_WireTransferIdentifierRawP wtid; - - /** - * Signature by the charity. - */ - struct DONAU_CharitySignatureP charity_sig; - - - /** - * Set by #handle_wtid data to the coin's contribution to the wire transfer. - */ - struct TALER_Amount coin_contribution; - - /** - * Set by #handle_wtid data to the fee charged to the coin. - */ - struct TALER_Amount coin_fee; - - /** - * Set by #handle_wtid data to the wire transfer execution time. - */ - struct GNUNET_TIME_Timestamp execution_time; - - /** - * Timeout of the request, for long-polling. - */ - struct GNUNET_TIME_Absolute timeout; - - /** - * Set by #handle_wtid to the coin contribution to the transaction - * (that is, @e coin_contribution minus @e coin_fee). - */ - struct TALER_Amount coin_delta; - - /** - * KYC status information for the receiving account. - */ - struct DONAUDB_KycStatus kyc; - - /** - * AML status information for the receiving account. - */ - enum TALER_AmlDecisionState aml_decision; - - /** - * Set to #GNUNET_YES by #handle_wtid if the wire transfer is still pending - * (and the above were not set). - * Set to #GNUNET_SYSERR if there was a serious error. - */ - enum GNUNET_GenericReturnValue pending; - - /** - * #GNUNET_YES if we were suspended, #GNUNET_SYSERR - * if we were woken up due to shutdown. - */ - enum GNUNET_GenericReturnValue suspended; -}; - - -/** - * Head of DLL of suspended requests. - */ -static struct DepositWtidContext *dwc_head; - -/** - * Tail of DLL of suspended requests. - */ -static struct DepositWtidContext *dwc_tail; - - -void -DH_deposits_get_cleanup () -{ - struct DepositWtidContext *n; - for (struct DepositWtidContext *ctx = dwc_head; - NULL != ctx; - ctx = n) - { - n = ctx->next; - GNUNET_assert (GNUNET_YES == ctx->suspended); - ctx->suspended = GNUNET_SYSERR; - MHD_resume_connection (ctx->rc->connection); - GNUNET_CONTAINER_DLL_remove (dwc_head, - dwc_tail, - ctx); - } -} - - -/** - * A charity asked for details about a deposit. Provide - * them. Generates the 200 reply. - * - * @param connection connection to the client - * @param ctx details to respond with - * @return MHD result code - */ -static MHD_RESULT -reply_deposit_details ( - struct MHD_Connection *connection, - const struct DepositWtidContext *ctx) -{ - struct DONAU_DonauPublicKeyP pub; - struct TALER_DonauSignatureP sig; - enum TALER_ErrorCode ec; - - if (TALER_EC_NONE != - (ec = TALER_donau_online_confirm_wire_sign ( - &DH_keys_donau_sign_, - &ctx->h_wire, - &ctx->h_contract_terms, - &ctx->wtid, - &ctx->coin_pub, - ctx->execution_time, - &ctx->coin_delta, - &pub, - &sig))) - { - GNUNET_break (0); - return TALER_MHD_reply_with_ec (connection, - ec, - NULL); - } - return TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_OK, - GNUNET_JSON_pack_data_auto ("wtid", - &ctx->wtid), - GNUNET_JSON_pack_timestamp ("execution_time", - ctx->execution_time), - TALER_JSON_pack_amount ("coin_contribution", - &ctx->coin_delta), - GNUNET_JSON_pack_data_auto ("donau_sig", - &sig), - GNUNET_JSON_pack_data_auto ("donau_pub", - &pub)); -} - - -/** - * Execute a "deposits" GET. Returns the transfer information - * associated with the given deposit. - * - * If it returns a non-error code, the transaction logic MUST - * NOT queue a MHD response. IF it returns an hard error, the - * transaction logic MUST queue a MHD response and set @a mhd_ret. IF - * it returns the soft error code, the function MAY be called again to - * retry and MUST NOT queue a MHD response. - * - * @param cls closure of type `struct DepositWtidContext *` - * @param connection MHD request which triggered the transaction - * @param[out] mhd_ret set to MHD response status for @a connection, - * if transaction failed (!) - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -deposits_get_transaction (void *cls, - struct MHD_Connection *connection, - MHD_RESULT *mhd_ret) -{ - struct DepositWtidContext *ctx = cls; - enum GNUNET_DB_QueryStatus qs; - bool pending; - struct TALER_Amount fee; - - qs = DH_plugin->lookup_transfer_by_deposit (DH_plugin->cls, - &ctx->h_contract_terms, - &ctx->h_wire, - &ctx->coin_pub, - &ctx->charity, - &pending, - &ctx->wtid, - &ctx->execution_time, - &ctx->coin_contribution, - &fee, - &ctx->kyc, - &ctx->aml_decision); - if (0 > qs) - { - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - } - return qs; - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_DONAU_DEPOSITS_GET_NOT_FOUND, - NULL); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - if (0 > - TALER_amount_subtract (&ctx->coin_delta, - &ctx->coin_contribution, - &fee)) - { - GNUNET_break (0); - ctx->pending = GNUNET_SYSERR; - return qs; - } - ctx->pending = (pending) ? GNUNET_YES : GNUNET_NO; - return qs; -} - - -/** - * Function called on events received from Postgres. - * Wakes up long pollers. - * - * @param cls the `struct DepositWtidContext *` - * @param extra additional event data provided - * @param extra_size number of bytes in @a extra - */ -static void -db_event_cb (void *cls, - const void *extra, - size_t extra_size) -{ - struct DepositWtidContext *ctx = cls; - struct GNUNET_AsyncScopeSave old_scope; - - (void) extra; - (void) extra_size; - if (GNUNET_NO != ctx->suspended) - return; /* might get multiple wake-up events */ - GNUNET_CONTAINER_DLL_remove (dwc_head, - dwc_tail, - ctx); - GNUNET_async_scope_enter (&ctx->rc->async_scope_id, - &old_scope); - DH_check_invariants (); - ctx->suspended = GNUNET_NO; - MHD_resume_connection (ctx->rc->connection); - TALER_MHD_daemon_trigger (); - DH_check_invariants (); - GNUNET_async_scope_restore (&old_scope); -} - - -/** - * Lookup and return the wire transfer identifier. - * - * @param ctx context of the signed request to execute - * @return MHD result code - */ -static MHD_RESULT -handle_track_transaction_request ( - struct DepositWtidContext *ctx) -{ - struct MHD_Connection *connection = ctx->rc->connection; - - if ( (GNUNET_TIME_absolute_is_future (ctx->timeout)) && - (NULL == ctx->eh) ) - { - struct TALER_CoinDepositEventP rep = { - .header.size = htons (sizeof (rep)), - .header.type = htons (TALER_DBEVENT_DONAU_DEPOSIT_STATUS_CHANGED), - .charity_pub = ctx->charity - }; - - ctx->eh = DH_plugin->event_listen ( - DH_plugin->cls, - GNUNET_TIME_absolute_get_remaining (ctx->timeout), - &rep.header, - &db_event_cb, - ctx); - } - { - MHD_RESULT mhd_ret; - - if (GNUNET_OK != - DH_DB_run_transaction (connection, - "handle deposits GET", - DH_MT_REQUEST_OTHER, - &mhd_ret, - &deposits_get_transaction, - ctx)) - return mhd_ret; - } - if (GNUNET_SYSERR == ctx->pending) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_INVARIANT_FAILURE, - "wire fees exceed aggregate in database"); - if (GNUNET_YES == ctx->pending) - { - if ( (GNUNET_TIME_absolute_is_future (ctx->timeout)) && - (GNUNET_NO == ctx->suspended) ) - { - GNUNET_CONTAINER_DLL_insert (dwc_head, - dwc_tail, - ctx); - ctx->suspended = GNUNET_YES; - MHD_suspend_connection (connection); - return MHD_YES; - } - return TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_ACCEPTED, - GNUNET_JSON_pack_allow_null ( - (0 == ctx->kyc.requirement_row) - ? GNUNET_JSON_pack_string ("requirement_row", - NULL) - : GNUNET_JSON_pack_uint64 ("requirement_row", - ctx->kyc.requirement_row)), - GNUNET_JSON_pack_uint64 ("aml_decision", - (uint32_t) ctx->aml_decision), - GNUNET_JSON_pack_bool ("kyc_ok", - ctx->kyc.ok), - GNUNET_JSON_pack_timestamp ("execution_time", - ctx->execution_time)); - } - return reply_deposit_details (connection, - ctx); -} - - -/** - * Function called to clean up a context. - * - * @param rc request context with data to clean up - */ -static void -dwc_cleaner (struct DH_RequestContext *rc) -{ - struct DepositWtidContext *ctx = rc->rh_ctx; - - GNUNET_assert (GNUNET_NO == ctx->suspended); - if (NULL != ctx->eh) - { - DH_plugin->event_listen_cancel (DH_plugin->cls, - ctx->eh); - ctx->eh = NULL; - } - GNUNET_free (ctx); -} - - -MHD_RESULT -DH_handler_deposits_get (struct DH_RequestContext *rc, - const char *const args[4]) -{ - struct DepositWtidContext *ctx = rc->rh_ctx; - - if (NULL == ctx) - { - ctx = GNUNET_new (struct DepositWtidContext); - ctx->rc = rc; - rc->rh_ctx = ctx; - rc->rh_cleaner = &dwc_cleaner; - - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (args[0], - strlen (args[0]), - &ctx->h_wire, - sizeof (ctx->h_wire))) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_DONAU_DEPOSITS_GET_INVALID_H_WIRE, - args[0]); - } - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (args[1], - strlen (args[1]), - &ctx->charity, - sizeof (ctx->charity))) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_DONAU_DEPOSITS_GET_INVALID_CHARITY_PUB, - args[1]); - } - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (args[2], - strlen (args[2]), - &ctx->h_contract_terms, - sizeof (ctx->h_contract_terms))) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_DONAU_DEPOSITS_GET_INVALID_H_CONTRACT_TERMS, - args[2]); - } - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (args[3], - strlen (args[3]), - &ctx->coin_pub, - sizeof (ctx->coin_pub))) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_DONAU_DEPOSITS_GET_INVALID_COIN_PUB, - args[3]); - } - TALER_MHD_parse_request_arg_auto_t (rc->connection, - "charity_sig", - &ctx->charity_sig); - TALER_MHD_parse_request_timeout (rc->connection, - &ctx->timeout); - DH_METRICS_num_verifications[DH_MT_SIGNATURE_EDDSA]++; - { - if (GNUNET_OK != - TALER_charity_deposit_verify (&ctx->charity, - &ctx->coin_pub, - &ctx->h_contract_terms, - &ctx->h_wire, - &ctx->charity_sig)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_DONAU_DEPOSITS_GET_CHARITY_SIGNATURE_INVALID, - NULL); - } - } - } - - return handle_track_transaction_request (ctx); -} - - -/* end of taler-donau-httpd_deposits_get.c */ diff --git a/src/donau/donau-httpd_submit_receipts_get.h b/src/donau/donau-httpd_submit_receipts_get.h @@ -1,50 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2017, 2021 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 CHARITYABILITY 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-donau-httpd_deposits_get.h - * @brief Handle wire transfer tracking-related requests - * @author Christian Grothoff - */ -#ifndef DONAU_HTTPD_DEPOSITS_GET_H -#define DONAU_HTTPD_DEPOSITS_GET_H - -#include <gnunet/gnunet_util_lib.h> -#include <microhttpd.h> -#include "taler-donau-httpd.h" - - -/** - * Resume long pollers on GET /deposits. - */ -void -DH_deposits_get_cleanup (void); - - -/** - * Handle a "/deposits/$H_WIRE/$CHARITY_PUB/$H_CONTRACT_TERMS/$COIN_PUB" - * request. - * - * @param rc request context - * @param args array of additional options (length: 4, contains: - * h_wire, charity_pub, h_contract_terms and coin_pub) - * @return MHD result code - */ -MHD_RESULT -DH_handler_deposits_get (struct DH_RequestContext *rc, - const char *const args[4]); - - -#endif diff --git a/src/donau/donau-httpd_terms.c b/src/donau/donau-httpd_terms.c @@ -1,81 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2019, 2021 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 CHARITYABILITY 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-donau-httpd_terms.c - * @brief Handle /terms requests to return the terms of service - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include "taler/taler_mhd_lib.h" -#include "donau-httpd_responses.h" - -/** - * Our terms of service. - */ -//static struct TALER_MHD_Legal *tos; - - -/** - * Our privacy policy. - */ -//static struct TALER_MHD_Legal *pp; - - -// MHD_RESULT -// DH_handler_terms (struct DH_RequestContext *rc, -// const char *const args[]) -// { -// (void) args; -// return TALER_MHD_reply_legal (rc->connection, -// tos); -// } - - -// MHD_RESULT -// DH_handler_privacy (struct DH_RequestContext *rc, -// const char *const args[]) -// { -// (void) args; -// return TALER_MHD_reply_legal (rc->connection, -// pp); -// } - - -// void -// DH_load_terms (const struct GNUNET_CONFIGURATION_Handle *cfg) -// { -// tos = TALER_MHD_legal_load (cfg, -// "donau", -// "TERMS_DIR", -// "TERMS_ETAG"); -// if (NULL == tos) -// GNUNET_log (GNUNET_ERROR_TYPE_WARNING, -// "Terms of service not configured\n"); -// pp = TALER_MHD_legal_load (cfg, -// "donau", -// "PRIVACY_DIR", -// "PRIVACY_ETAG"); -// if (NULL == pp) -// GNUNET_log (GNUNET_ERROR_TYPE_WARNING, -// "Privacy policy not configured\n"); -// } - - -/* end of taler-donau-httpd_terms.c */ diff --git a/src/donau/donau-httpd_terms.h b/src/donau/donau-httpd_terms.h @@ -1,65 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2019, 2021 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 CHARITYABILITY 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 <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-donau-httpd_terms.h - * @brief Handle /terms requests to return the terms of service - * @author Christian Grothoff - */ -#ifndef DONAU_HTTPD_TERMS_H -#define DONAU_HTTPD_TERMS_H -#include "taler/platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include "taler/taler_mhd_lib.h" -#include "donau-httpd_responses.h" - - -/** - * Handle a "/terms" request. - * - * @param rc request context - * @param args array of additional options (must be empty for this function) - * @return MHD result code - */ -MHD_RESULT -DH_handler_terms (struct DH_RequestContext *rc, - const char *const args[]); - - -/** - * Handle a "/privacy" request. - * - * @param rc request context - * @param args array of additional options (must be empty for this function) - * @return MHD result code - */ -MHD_RESULT -DH_handler_privacy (struct DH_RequestContext *rc, - const char *const args[]); - - -/** - * Load our terms of service as per configuration. - * - * @param cfg configuration to process - */ -void -DH_load_terms (const struct GNUNET_CONFIGURATION_Handle *cfg); - - -#endif diff --git a/src/donaudb/pg_add_donation_unit_key.h b/src/donaudb/pg_add_donation_unit_key.h @@ -26,8 +26,7 @@ #include "donaudb_plugin.h" /** - * Activate donation unit key, turning it into a "current" or "valid" - * donation_unit key by adding the master signature. + * Add donation unit key. * * @param cls closure * @param donation_unit_pub the actual donation_unit key diff --git a/src/lib/donau_api_batch_issue_receipts.c b/src/lib/donau_api_batch_issue_receipts.c @@ -1,456 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2022 Taler Systems SA - - 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 CHARITYABILITY 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, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file lib/donau_api_batch_withdraw.c - * @brief Implementation of /reserves/$RESERVE_PUB/batch-withdraw requests with blinding/unblinding - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler_donau_service.h" -#include "taler/taler_json_lib.h" -#include "donau_api_handle.h" -#include "taler_signatures.h" -#include "donau_api_curl_defaults.h" - - -/** - * Data we keep per coin in the batch. - */ -struct CoinData -{ - - /** - * Denomination key we are withdrawing. - */ - struct DONAU_DenomPublicKey pk; - - /** - * Master key material for the coin. - */ - struct TALER_PlanchetMasterSecretP ps; - - /** - * Age commitment for the coin. - */ - const struct TALER_AgeCommitmentHash *ach; - - /** - * blinding secret - */ - union TALER_DenominationBlindingKeyP bks; - - /** - * Private key of the coin we are withdrawing. - */ - struct TALER_CoinSpendPrivateKeyP priv; - - /** - * Details of the planchet. - */ - struct TALER_PlanchetDetail pd; - - /** - * Values of the @cipher selected - */ - struct TALER_DonauWithdrawValues alg_values; - - /** - * Hash of the public key of the coin we are signing. - */ - struct TALER_CoinPubHashP c_hash; - - /** - * Handler for the CS R request (only used for TALER_DENOMINATION_CS denominations) - */ - struct DONAU_CsRWithdrawHandle *csrh; - - /** - * Batch withdraw this coin is part of. - */ - struct DONAU_BatchWithdrawHandle *wh; -}; - - -/** - * @brief A batch withdraw handle - */ -struct DONAU_BatchWithdrawHandle -{ - - /** - * The curl context to use - */ - struct GNUNET_CURL_Context *curl_ctx; - - /** - * The base URL to the donau - */ - const char *donau_url; - - /** - * The /keys information from the donau - */ - const struct DONAU_Keys *keys; - - - /** - * Handle for the actual (internal) batch withdraw operation. - */ - struct DONAU_BatchWithdraw2Handle *wh2; - - /** - * Function to call with the result. - */ - DONAU_BatchWithdrawCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Reserve private key. - */ - const struct TALER_ReservePrivateKeyP *reserve_priv; - - /** - * Array of per-coin data. - */ - struct CoinData *coins; - - /** - * Length of the @e coins array. - */ - unsigned int num_coins; - - /** - * Number of CS requests still pending. - */ - unsigned int cs_pending; - -}; - - -/** - * Function called when we're done processing the - * HTTP /reserves/$RESERVE_PUB/batch-withdraw request. - * - * @param cls the `struct DONAU_BatchWithdrawHandle` - * @param bw2r response data - */ -static void -handle_reserve_batch_withdraw_finished ( - void *cls, - const struct DONAU_BatchWithdraw2Response *bw2r) -{ - struct DONAU_BatchWithdrawHandle *wh = cls; - struct DONAU_BatchWithdrawResponse wr = { - .hr = bw2r->hr - }; - struct DONAU_PrivateCoinDetails coins[GNUNET_NZL (wh->num_coins)]; - - wh->wh2 = NULL; - memset (coins, - 0, - sizeof (coins)); - switch (bw2r->hr.http_status) - { - case MHD_HTTP_OK: - { - if (bw2r->details.ok.blind_sigs_length != wh->num_coins) - { - GNUNET_break_op (0); - wr.hr.http_status = 0; - wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - for (unsigned int i = 0; i<wh->num_coins; i++) - { - struct CoinData *cd = &wh->coins[i]; - struct DONAU_PrivateCoinDetails *coin = &coins[i]; - struct TALER_FreshCoin fc; - - if (GNUNET_OK != - TALER_planchet_to_coin (&cd->pk.key, - &bw2r->details.ok.blind_sigs[i], - &cd->bks, - &cd->priv, - cd->ach, - &cd->c_hash, - &cd->alg_values, - &fc)) - { - wr.hr.http_status = 0; - wr.hr.ec = TALER_EC_DONAU_WITHDRAW_UNBLIND_FAILURE; - break; - } - coin->coin_priv = cd->priv; - coin->bks = cd->bks; - coin->sig = fc.sig; - coin->donau_vals = cd->alg_values; - } - wr.details.ok.coins = coins; - wr.details.ok.num_coins = wh->num_coins; - break; - } - case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ( - "h_payto", - &wr.details.unavailable_for_legal_reasons.h_payto), - GNUNET_JSON_spec_uint64 ( - "requirement_row", - &wr.details.unavailable_for_legal_reasons.requirement_row), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (bw2r->hr.reply, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - wr.hr.http_status = 0; - wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - } - break; - default: - break; - } - wh->cb (wh->cb_cls, - &wr); - for (unsigned int i = 0; i<wh->num_coins; i++) - TALER_denom_sig_free (&coins[i].sig); - DONAU_batch_withdraw_cancel (wh); -} - - -/** - * Runs phase two, the actual withdraw operation. - * Started once the preparation for CS-denominations is - * done. - * - * @param[in,out] wh batch withdraw to start phase 2 for - */ -static void -phase_two (struct DONAU_BatchWithdrawHandle *wh) -{ - struct TALER_PlanchetDetail pds[wh->num_coins]; - - for (unsigned int i = 0; i<wh->num_coins; i++) - { - struct CoinData *cd = &wh->coins[i]; - - pds[i] = cd->pd; - } - wh->wh2 = DONAU_batch_withdraw2 ( - wh->curl_ctx, - wh->donau_url, - wh->keys, - wh->reserve_priv, - wh->num_coins, - pds, - &handle_reserve_batch_withdraw_finished, - wh); -} - - -/** - * Function called when stage 1 of CS withdraw is finished (request r_pub's) - * - * @param cls the `struct CoinData *` - * @param csrr replies from the /csr-withdraw request - */ -static void -withdraw_cs_stage_two_callback ( - void *cls, - const struct DONAU_CsRWithdrawResponse *csrr) -{ - struct CoinData *cd = cls; - struct DONAU_BatchWithdrawHandle *wh = cd->wh; - struct DONAU_BatchWithdrawResponse wr = { - .hr = csrr->hr - }; - - cd->csrh = NULL; - GNUNET_assert (TALER_DENOMINATION_CS == cd->pk.key.cipher); - switch (csrr->hr.http_status) - { - case MHD_HTTP_OK: - cd->alg_values = csrr->details.ok.alg_values; - TALER_planchet_setup_coin_priv (&cd->ps, - &cd->alg_values, - &cd->priv); - TALER_planchet_blinding_secret_create (&cd->ps, - &cd->alg_values, - &cd->bks); - /* This initializes the 2nd half of the - wh->pd.blinded_planchet! */ - if (GNUNET_OK != - TALER_planchet_prepare (&cd->pk.key, - &cd->alg_values, - &cd->bks, - &cd->priv, - cd->ach, - &cd->c_hash, - &cd->pd)) - { - GNUNET_break (0); - DONAU_batch_withdraw_cancel (wh); - } - wh->cs_pending--; - if (0 == wh->cs_pending) - phase_two (wh); - return; - default: - break; - } - wh->cb (wh->cb_cls, - &wr); - DONAU_batch_withdraw_cancel (wh); -} - - -struct DONAU_BatchWithdrawHandle * -DONAU_batch_withdraw ( - struct GNUNET_CURL_Context *curl_ctx, - const char *donau_url, - const struct DONAU_Keys *keys, - const struct TALER_ReservePrivateKeyP *reserve_priv, - unsigned int wci_length, - const struct DONAU_WithdrawCoinInput wcis[static wci_length], - DONAU_BatchWithdrawCallback res_cb, - void *res_cb_cls) -{ - struct DONAU_BatchWithdrawHandle *wh; - - wh = GNUNET_new (struct DONAU_BatchWithdrawHandle); - wh->curl_ctx = curl_ctx; - wh->donau_url = donau_url; - wh->keys = keys; - wh->cb = res_cb; - wh->cb_cls = res_cb_cls; - wh->reserve_priv = reserve_priv; - wh->num_coins = wci_length; - wh->coins = GNUNET_new_array (wh->num_coins, - struct CoinData); - for (unsigned int i = 0; i<wci_length; i++) - { - struct CoinData *cd = &wh->coins[i]; - const struct DONAU_WithdrawCoinInput *wci = &wcis[i]; - - cd->wh = wh; - cd->ps = *wci->ps; - cd->ach = wci->ach; - cd->pk = *wci->pk; - TALER_denom_pub_deep_copy (&cd->pk.key, - &wci->pk->key); - switch (wci->pk->key.cipher) - { - case TALER_DENOMINATION_RSA: - { - cd->alg_values.cipher = TALER_DENOMINATION_RSA; - TALER_planchet_setup_coin_priv (&cd->ps, - &cd->alg_values, - &cd->priv); - TALER_planchet_blinding_secret_create (&cd->ps, - &cd->alg_values, - &cd->bks); - if (GNUNET_OK != - TALER_planchet_prepare (&cd->pk.key, - &cd->alg_values, - &cd->bks, - &cd->priv, - cd->ach, - &cd->c_hash, - &cd->pd)) - { - GNUNET_break (0); - DONAU_batch_withdraw_cancel (wh); - return NULL; - } - break; - } - case TALER_DENOMINATION_CS: - { - TALER_cs_withdraw_nonce_derive ( - &cd->ps, - &cd->pd.blinded_planchet.details.cs_blinded_planchet.nonce); - /* Note that we only initialize the first half - of the blinded_planchet here; the other part - will be done after the /csr-withdraw request! */ - cd->pd.blinded_planchet.cipher = TALER_DENOMINATION_CS; - cd->csrh = DONAU_csr_withdraw ( - curl_ctx, - donau_url, - &cd->pk, - &cd->pd.blinded_planchet.details.cs_blinded_planchet.nonce, - &withdraw_cs_stage_two_callback, - cd); - if (NULL == cd->csrh) - { - GNUNET_break (0); - DONAU_batch_withdraw_cancel (wh); - return NULL; - } - wh->cs_pending++; - break; - } - default: - GNUNET_break (0); - DONAU_batch_withdraw_cancel (wh); - return NULL; - } - } - if (0 == wh->cs_pending) - phase_two (wh); - return wh; -} - - -void -DONAU_batch_withdraw_cancel ( - struct DONAU_BatchWithdrawHandle *wh) -{ - for (unsigned int i = 0; i<wh->num_coins; i++) - { - struct CoinData *cd = &wh->coins[i]; - - if (NULL != cd->csrh) - { - DONAU_csr_withdraw_cancel (cd->csrh); - cd->csrh = NULL; - } - TALER_blinded_planchet_free (&cd->pd.blinded_planchet); - TALER_denom_pub_free (&cd->pk.key); - } - GNUNET_free (wh->coins); - if (NULL != wh->wh2) - { - DONAU_batch_withdraw2_cancel (wh->wh2); - wh->wh2 = NULL; - } - GNUNET_free (wh); -} diff --git a/src/lib/donau_api_batch_issue_receipts2.c b/src/lib/donau_api_batch_issue_receipts2.c @@ -1,555 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2023 Taler Systems SA - - 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 CHARITYABILITY 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, see - <http://www.gnu.org/licenses/> -*/ -/** - * @file lib/donau_api_batch_withdraw2.c - * @brief Implementation of /reserves/$RESERVE_PUB/batch-withdraw requests without blinding/unblinding - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler_donau_service.h" -#include "taler/taler_json_lib.h" -#include "donau_api_handle.h" -#include "taler_signatures.h" -#include "donau_api_curl_defaults.h" - - -/** - * @brief A batch withdraw handle - */ -struct DONAU_BatchWithdraw2Handle -{ - - /** - * The url for this request. - */ - char *url; - - /** - * The /keys material from the donau - */ - const struct DONAU_Keys *keys; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - DONAU_BatchWithdraw2Callback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Context for #DH_curl_easy_post(). Keeps the data that must - * persist for Curl to make the upload. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Total amount requested (value plus withdraw fee). - */ - struct TALER_Amount requested_amount; - - /** - * Public key of the reserve we are withdrawing from. - */ - struct TALER_ReservePublicKeyP reserve_pub; - - /** - * Number of coins expected. - */ - unsigned int num_coins; -}; - - -/** - * We got a 200 OK response for the /reserves/$RESERVE_PUB/batch-withdraw operation. - * Extract the coin's signature and return it to the caller. The signature we - * get from the donau is for the blinded value. Thus, we first must - * unblind it and then should verify its validity against our coin's hash. - * - * If everything checks out, we return the unblinded signature - * to the application via the callback. - * - * @param wh operation handle - * @param json reply from the donau - * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors - */ -static enum GNUNET_GenericReturnValue -reserve_batch_withdraw_ok (struct DONAU_BatchWithdraw2Handle *wh, - const json_t *json) -{ - struct TALER_BlindedDenominationSignature blind_sigs[wh->num_coins]; - const json_t *ja = json_object_get (json, - "ev_sigs"); - const json_t *j; - unsigned int index; - struct DONAU_BatchWithdraw2Response bwr = { - .hr.reply = json, - .hr.http_status = MHD_HTTP_OK - }; - - if ( (NULL == ja) || - (! json_is_array (ja)) || - (wh->num_coins != json_array_size (ja)) ) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - json_array_foreach (ja, index, j) - { - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_blinded_denom_sig ("ev_sig", - &blind_sigs[index]), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (j, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - for (unsigned int i = 0; i<index; i++) - TALER_blinded_denom_sig_free (&blind_sigs[i]); - return GNUNET_SYSERR; - } - } - - /* signature is valid, return it to the application */ - bwr.details.ok.blind_sigs = blind_sigs; - bwr.details.ok.blind_sigs_length = wh->num_coins; - wh->cb (wh->cb_cls, - &bwr); - /* make sure callback isn't called again after return */ - wh->cb = NULL; - for (unsigned int i = 0; i<wh->num_coins; i++) - TALER_blinded_denom_sig_free (&blind_sigs[i]); - - return GNUNET_OK; -} - - -/** - * We got a 409 CONFLICT response for the /reserves/$RESERVE_PUB/batch-withdraw operation. - * Check the signatures on the batch withdraw transactions in the provided - * history and that the balances add up. We don't do anything directly - * with the information, as the JSON will be returned to the application. - * However, our job is ensuring that the donau followed the protocol, and - * this in particular means checking all of the signatures in the history. - * - * @param wh operation handle - * @param json reply from the donau - * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors - */ -static enum GNUNET_GenericReturnValue -reserve_batch_withdraw_payment_required ( - struct DONAU_BatchWithdraw2Handle *wh, - const json_t *json) -{ - struct TALER_Amount balance; - struct TALER_Amount total_in_from_history; - struct TALER_Amount total_out_from_history; - json_t *history; - size_t len; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount_any ("balance", - &balance), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - history = json_object_get (json, - "history"); - if (NULL == history) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - /* go over transaction history and compute - total incoming and outgoing amounts */ - len = json_array_size (history); - { - struct DONAU_ReserveHistoryEntry *rhistory; - - /* Use heap allocation as "len" may be very big and thus this may - not fit on the stack. Use "GNUNET_malloc_large" as a malicious - donau may theoretically try to crash us by giving a history - that does not fit into our memory. */ - rhistory = GNUNET_malloc_large ( - sizeof (struct DONAU_ReserveHistoryEntry) - * len); - if (NULL == rhistory) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - DONAU_parse_reserve_history ( - wh->keys, - history, - &wh->reserve_pub, - balance.currency, - &total_in_from_history, - &total_out_from_history, - len, - rhistory)) - { - GNUNET_break_op (0); - DONAU_free_reserve_history (len, - rhistory); - return GNUNET_SYSERR; - } - DONAU_free_reserve_history (len, - rhistory); - } - - /* Check that funds were really insufficient */ - if (0 >= TALER_amount_cmp (&wh->requested_amount, - &balance)) - { - /* Requested amount is smaller or equal to reported balance, - so this should not have failed. */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /reserves/$RESERVE_PUB/batch-withdraw request. - * - * @param cls the `struct DONAU_BatchWithdraw2Handle` - * @param response_code HTTP response code, 0 on error - * @param response parsed JSON result, NULL on error - */ -static void -handle_reserve_batch_withdraw_finished (void *cls, - long response_code, - const void *response) -{ - struct DONAU_BatchWithdraw2Handle *wh = cls; - const json_t *j = response; - struct DONAU_BatchWithdraw2Response bwr = { - .hr.reply = j, - .hr.http_status = (unsigned int) response_code - }; - - wh->job = NULL; - switch (response_code) - { - case 0: - bwr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - if (GNUNET_OK != - reserve_batch_withdraw_ok (wh, - j)) - { - GNUNET_break_op (0); - bwr.hr.http_status = 0; - bwr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - GNUNET_assert (NULL == wh->cb); - DONAU_batch_withdraw2_cancel (wh); - return; - case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: - /* only validate reply is well-formed */ - { - uint64_t ptu; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_uint64 ("legitimization_uuid", - &ptu), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (j, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - bwr.hr.http_status = 0; - bwr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - } - break; - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the donau is buggy - (or API version conflict); just pass JSON reply to the application */ - bwr.hr.ec = TALER_JSON_get_error_code (j); - bwr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_FORBIDDEN: - GNUNET_break_op (0); - /* Nothing really to verify, donau says one of the signatures is - invalid; as we checked them, this should never happen, we - should pass the JSON reply to the application */ - bwr.hr.ec = TALER_JSON_get_error_code (j); - bwr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, the donau basically just says - that it doesn't know this reserve. Can happen if we - query before the wire transfer went through. - We should simply pass the JSON reply to the application. */ - bwr.hr.ec = TALER_JSON_get_error_code (j); - bwr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_CONFLICT: - /* The donau says that the reserve has insufficient funds; - check the signatures in the history... */ - if (GNUNET_OK != - reserve_batch_withdraw_payment_required (wh, - j)) - { - GNUNET_break_op (0); - bwr.hr.http_status = 0; - bwr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - } - else - { - bwr.hr.ec = TALER_JSON_get_error_code (j); - bwr.hr.hint = TALER_JSON_get_error_hint (j); - } - break; - case MHD_HTTP_GONE: - /* could happen if denomination was revoked */ - /* Note: one might want to check /keys for revocation - signature here, alas tricky in case our /keys - is outdated => left to clients */ - bwr.hr.ec = TALER_JSON_get_error_code (j); - bwr.hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - bwr.hr.ec = TALER_JSON_get_error_code (j); - bwr.hr.hint = TALER_JSON_get_error_hint (j); - break; - default: - /* unexpected response code */ - GNUNET_break_op (0); - bwr.hr.ec = TALER_JSON_get_error_code (j); - bwr.hr.hint = TALER_JSON_get_error_hint (j); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for donau batch withdraw\n", - (unsigned int) response_code, - (int) bwr.hr.ec); - break; - } - if (NULL != wh->cb) - { - wh->cb (wh->cb_cls, - &bwr); - wh->cb = NULL; - } - DONAU_batch_withdraw2_cancel (wh); -} - - -struct DONAU_BatchWithdraw2Handle * -DONAU_batch_withdraw2 ( - struct GNUNET_CURL_Context *curl_ctx, - const char *donau_url, - const struct DONAU_Keys *keys, - const struct TALER_ReservePrivateKeyP *reserve_priv, - unsigned int pds_length, - const struct TALER_PlanchetDetail pds[static pds_length], - DONAU_BatchWithdraw2Callback res_cb, - void *res_cb_cls) -{ - struct DONAU_BatchWithdraw2Handle *wh; - const struct DONAU_DenomPublicKey *dk; - struct TALER_ReserveSignatureP reserve_sig; - char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32]; - struct TALER_BlindedCoinHashP bch; - json_t *jc; - - GNUNET_assert (NULL != keys); - wh = GNUNET_new (struct DONAU_BatchWithdraw2Handle); - wh->keys = keys; - wh->cb = res_cb; - wh->cb_cls = res_cb_cls; - wh->num_coins = pds_length; - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (keys->currency, - &wh->requested_amount)); - GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, - &wh->reserve_pub.eddsa_pub); - { - char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string ( - &wh->reserve_pub, - sizeof (struct TALER_ReservePublicKeyP), - pub_str, - sizeof (pub_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "reserves/%s/batch-withdraw", - pub_str); - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Attempting to batch-withdraw from reserve %s\n", - TALER_B2S (&wh->reserve_pub)); - wh->url = TALER_url_join (donau_url, - arg_str, - NULL); - if (NULL == wh->url) - { - GNUNET_break (0); - DONAU_batch_withdraw2_cancel (wh); - return NULL; - } - jc = json_array (); - GNUNET_assert (NULL != jc); - for (unsigned int i = 0; i<pds_length; i++) - { - const struct TALER_PlanchetDetail *pd = &pds[i]; - struct TALER_Amount coin_total; - json_t *withdraw_obj; - - dk = DONAU_get_denomination_key_by_hash (keys, - &pd->denom_pub_hash); - if (NULL == dk) - { - DONAU_batch_withdraw2_cancel (wh); - json_decref (jc); - GNUNET_break (0); - return NULL; - } - /* Compute how much we expected to charge to the reserve */ - if (0 > - TALER_amount_add (&coin_total, - &dk->fees.withdraw, - &dk->value)) - { - /* Overflow here? Very strange, our CPU must be fried... */ - GNUNET_break (0); - DONAU_batch_withdraw2_cancel (wh); - json_decref (jc); - return NULL; - } - if (0 > - TALER_amount_add (&wh->requested_amount, - &wh->requested_amount, - &coin_total)) - { - /* Overflow here? Very strange, our CPU must be fried... */ - GNUNET_break (0); - DONAU_batch_withdraw2_cancel (wh); - json_decref (jc); - return NULL; - } - if (GNUNET_OK != - TALER_coin_ev_hash (&pd->blinded_planchet, - &pd->denom_pub_hash, - &bch)) - { - GNUNET_break (0); - DONAU_batch_withdraw2_cancel (wh); - json_decref (jc); - return NULL; - } - TALER_wallet_withdraw_sign (&pd->denom_pub_hash, - &coin_total, - &bch, - reserve_priv, - &reserve_sig); - withdraw_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("denom_pub_hash", - &pd->denom_pub_hash), - TALER_JSON_pack_blinded_planchet ("coin_ev", - &pd->blinded_planchet), - GNUNET_JSON_pack_data_auto ("reserve_sig", - &reserve_sig)); - GNUNET_assert (NULL != withdraw_obj); - GNUNET_assert (0 == - json_array_append_new (jc, - withdraw_obj)); - } - { - CURL *eh; - json_t *req; - - req = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("planchets", - jc)); - eh = DONAU_curl_easy_get_ (wh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&wh->post_ctx, - eh, - req)) ) - { - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (req); - DONAU_batch_withdraw2_cancel (wh); - return NULL; - } - json_decref (req); - wh->job = GNUNET_CURL_job_add2 (curl_ctx, - eh, - wh->post_ctx.headers, - &handle_reserve_batch_withdraw_finished, - wh); - } - return wh; -} - - -void -DONAU_batch_withdraw2_cancel ( - struct DONAU_BatchWithdraw2Handle *wh) -{ - if (NULL != wh->job) - { - GNUNET_CURL_job_cancel (wh->job); - wh->job = NULL; - } - GNUNET_free (wh->url); - TALER_curl_easy_post_finished (&wh->post_ctx); - GNUNET_free (wh); -} diff --git a/src/lib/donau_api_batch_submit_receipts.c b/src/lib/donau_api_batch_submit_receipts.c @@ -1,779 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2022 Taler Systems SA - - 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 CHARITYABILITY 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, see - <http://www.gnu.org/licenses/> - */ -/** - * @file lib/donau_api_batch_deposit.c - * @brief Implementation of the /batch-deposit request of the donau's HTTP API - * @author Sree Harsha Totakura <sreeharsha@totakura.in> - * @author Christian Grothoff - */ -#include "taler/platform.h" -#include <jansson.h> -#include <microhttpd.h> /* just for HTTP status codes */ -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <gnunet/gnunet_curl_lib.h> -#include "taler/taler_json_lib.h" -#include "taler_auditor_service.h" -#include "taler_donau_service.h" -#include "donau_api_common.h" -#include "donau_api_handle.h" -#include "taler_signatures.h" -#include "donau_api_curl_defaults.h" - - -/** - * 1:#AUDITOR_CHANCE is the probability that we report deposits - * to the auditor. - * - * 20==5% of going to auditor. This is possibly still too high, but set - * deliberately this high for testing - */ -#define AUDITOR_CHANCE 20 - - -/** - * Entry in list of ongoing interactions with an auditor. - */ -struct TEAH_AuditorInteractionEntry -{ - /** - * DLL entry. - */ - struct TEAH_AuditorInteractionEntry *next; - - /** - * DLL entry. - */ - struct TEAH_AuditorInteractionEntry *prev; - - /** - * URL of our auditor. For logging. - */ - const char *auditor_url; - - /** - * Interaction state. - */ - struct TALER_AUDITOR_DepositConfirmationHandle *dch; - - /** - * Batch deposit this is for. - */ - struct DONAU_BatchDepositHandle *dh; -}; - - -/** - * @brief A Deposit Handle - */ -struct DONAU_BatchDepositHandle -{ - - /** - * The keys of the donau. - */ - struct DONAU_Keys *keys; - - /** - * Context for our curl request(s). - */ - struct GNUNET_CURL_Context *ctx; - - /** - * The url for this request. - */ - char *url; - - /** - * Context for #DH_curl_easy_post(). Keeps the data that must - * persist for Curl to make the upload. - */ - struct TALER_CURL_PostContext post_ctx; - - /** - * Handle for the request. - */ - struct GNUNET_CURL_Job *job; - - /** - * Function to call with the result. - */ - DONAU_BatchDepositResultCallback cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Details about the contract. - */ - struct DONAU_DepositContractDetail dcd; - - /** - * Array with details about the coins. - */ - struct DONAU_CoinDepositDetail *cdds; - - /** - * Hash of the charity's wire details. - */ - struct TALER_CharityWireHashP h_wire; - - /** - * Time when this confirmation was generated / when the donau received - * the deposit request. - */ - struct GNUNET_TIME_Timestamp donau_timestamp; - - /** - * Donau signatures, set for #auditor_cb. - */ - struct TALER_DonauSignatureP *donau_sigs; - - /** - * Head of DLL of interactions with this auditor. - */ - struct TEAH_AuditorInteractionEntry *ai_head; - - /** - * Tail of DLL of interactions with this auditor. - */ - struct TEAH_AuditorInteractionEntry *ai_tail; - - /** - * Result to return to the application once @e ai_head is empty. - */ - struct DONAU_BatchDepositResult dr; - - /** - * Donau signing public key, set for #auditor_cb. - */ - struct DONAU_DonauPublicKeyP donau_pub; - - /** - * Response object to free at the end. - */ - json_t *response; - - /** - * Chance that we will inform the auditor about the deposit - * is 1:n, where the value of this field is "n". - */ - unsigned int auditor_chance; - - /** - * Length of the @e cdds array. - */ - unsigned int num_cdds; - -}; - - -/** - * Finish batch deposit operation by calling the callback. - * - * @param[in] dh handle to finished batch deposit operation - */ -static void -finish_dh (struct DONAU_BatchDepositHandle *dh) -{ - dh->cb (dh->cb_cls, - &dh->dr); - DONAU_batch_deposit_cancel (dh); -} - - -/** - * Function called with the result from our call to the - * auditor's /deposit-confirmation handler. - * - * @param cls closure of type `struct TEAH_AuditorInteractionEntry *` - * @param dcr response - */ -static void -acc_confirmation_cb ( - void *cls, - const struct TALER_AUDITOR_DepositConfirmationResponse *dcr) -{ - struct TEAH_AuditorInteractionEntry *aie = cls; - struct DONAU_BatchDepositHandle *dh = aie->dh; - - if (MHD_HTTP_OK != dcr->hr.http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to submit deposit confirmation to auditor `%s' with HTTP status %d (EC: %d). This is acceptable if it does not happen often.\n", - aie->auditor_url, - dcr->hr.http_status, - dcr->hr.ec); - } - GNUNET_CONTAINER_DLL_remove (dh->ai_head, - dh->ai_tail, - aie); - GNUNET_free (aie); - if (NULL == dh->ai_head) - finish_dh (dh); -} - - -/** - * Function called for each auditor to give us a chance to possibly - * launch a deposit confirmation interaction. - * - * @param cls closure - * @param auditor_url base URL of the auditor - * @param auditor_pub public key of the auditor - */ -static void -auditor_cb (void *cls, - const char *auditor_url, - const struct TALER_AuditorPublicKeyP *auditor_pub) -{ - struct DONAU_BatchDepositHandle *dh = cls; - const struct DONAU_SigningPublicKey *spk; - struct TEAH_AuditorInteractionEntry *aie; - struct TALER_Amount amount_without_fee; - const struct DONAU_DenomPublicKey *dki; - unsigned int coin; - - if (0 != - GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, - dh->auditor_chance)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Not providing deposit confirmation to auditor\n"); - return; - } - coin = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, - dh->num_cdds); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Will provide deposit confirmation to auditor `%s'\n", - TALER_B2S (auditor_pub)); - dki = DONAU_get_denomination_key_by_hash (dh->keys, - &dh->cdds[coin].h_denom_pub); - GNUNET_assert (NULL != dki); - spk = DONAU_get_signing_key_info (dh->keys, - &dh->donau_pub); - if (NULL == spk) - { - GNUNET_break_op (0); - return; - } - GNUNET_assert (0 <= - TALER_amount_subtract (&amount_without_fee, - &dh->cdds[coin].amount, - &dki->fees.deposit)); - aie = GNUNET_new (struct TEAH_AuditorInteractionEntry); - aie->dh = dh; - aie->auditor_url = auditor_url; - aie->dch = TALER_AUDITOR_deposit_confirmation ( - dh->ctx, - auditor_url, - &dh->h_wire, - &dh->h_policy, - &dh->dcd.h_contract_terms, - dh->donau_timestamp, - dh->dcd.wire_deadline, - dh->dcd.refund_deadline, - &amount_without_fee, - &dh->cdds[coin].coin_pub, - &dh->dcd.charity_pub, - &dh->donau_pub, - &dh->donau_sigs[coin], - &dh->keys->master_pub, - spk->valid_from, - spk->valid_until, - spk->valid_legal, - &spk->master_sig, - &acc_confirmation_cb, - aie); - GNUNET_CONTAINER_DLL_insert (dh->ai_head, - dh->ai_tail, - aie); -} - - -/** - * Function called when we're done processing the - * HTTP /deposit request. - * - * @param cls the `struct DONAU_BatchDepositHandle` - * @param response_code HTTP response code, 0 on error - * @param response parsed JSON result, NULL on error - */ -static void -handle_deposit_finished (void *cls, - long response_code, - const void *response) -{ - struct DONAU_BatchDepositHandle *dh = cls; - const json_t *j = response; - struct DONAU_BatchDepositResult *dr = &dh->dr; - - dh->job = NULL; - dh->response = json_incref ((json_t*) j); - dr->hr.reply = dh->response; - dr->hr.http_status = (unsigned int) response_code; - switch (response_code) - { - case 0: - dr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - case MHD_HTTP_OK: - { - const json_t *sigs; - json_t *sig; - unsigned int idx; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("donau_sigs", - &sigs), - GNUNET_JSON_spec_fixed_auto ("donau_pub", - &dh->donau_pub), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("transaction_base_url", - &dr->details.ok.transaction_base_url), - NULL), - GNUNET_JSON_spec_timestamp ("donau_timestamp", - &dh->donau_timestamp), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (j, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - dr->hr.http_status = 0; - dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - if (json_array_size (sigs) != dh->num_cdds) - { - GNUNET_break_op (0); - dr->hr.http_status = 0; - dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - dh->donau_sigs = GNUNET_new_array (dh->num_cdds, - struct TALER_DonauSignatureP); - if (GNUNET_OK != - DONAU_test_signing_key (dh->keys, - &dh->donau_pub)) - { - GNUNET_break_op (0); - dr->hr.http_status = 0; - dr->hr.ec = TALER_EC_DONAU_DEPOSIT_INVALID_SIGNATURE_BY_DONAU; - break; - } - json_array_foreach (sigs, idx, sig) - { - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_fixed_auto ("donau_sig", - &dh->donau_sigs[idx]), - GNUNET_JSON_spec_end () - }; - struct TALER_Amount amount_without_fee; - const struct DONAU_DenomPublicKey *dki; - - if (GNUNET_OK != - GNUNET_JSON_parse (sig, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - dr->hr.http_status = 0; - dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - dki = DONAU_get_denomination_key_by_hash (dh->keys, - &dh->cdds[idx]. - h_denom_pub); - GNUNET_assert (NULL != dki); - GNUNET_assert (0 <= - TALER_amount_subtract (&amount_without_fee, - &dh->cdds[idx].amount, - &dki->fees.deposit)); - - if (GNUNET_OK != - TALER_donau_online_deposit_confirmation_verify ( - &dh->dcd.h_contract_terms, - &dh->h_wire, - &dh->h_policy, - dh->donau_timestamp, - dh->dcd.wire_deadline, - dh->dcd.refund_deadline, - &amount_without_fee, - &dh->cdds[idx].coin_pub, - &dh->dcd.charity_pub, - &dh->donau_pub, - &dh->donau_sigs[idx])) - { - GNUNET_break_op (0); - dr->hr.http_status = 0; - dr->hr.ec = TALER_EC_DONAU_DEPOSIT_INVALID_SIGNATURE_BY_DONAU; - break; - } - } - TEAH_get_auditors_for_dc (dh->keys, - &auditor_cb, - dh); - } - dr->details.ok.donau_sigs = dh->donau_sigs; - dr->details.ok.donau_pub = &dh->donau_pub; - dr->details.ok.deposit_timestamp = dh->donau_timestamp; - dr->details.ok.num_signatures = dh->num_cdds; - break; - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the donau is buggy - (or API version conflict); just pass JSON reply to the application */ - dr->hr.ec = TALER_JSON_get_error_code (j); - dr->hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_FORBIDDEN: - dr->hr.ec = TALER_JSON_get_error_code (j); - dr->hr.hint = TALER_JSON_get_error_hint (j); - /* Nothing really to verify, donau says one of the signatures is - invalid; as we checked them, this should never happen, we - should pass the JSON reply to the application */ - break; - case MHD_HTTP_NOT_FOUND: - dr->hr.ec = TALER_JSON_get_error_code (j); - dr->hr.hint = TALER_JSON_get_error_hint (j); - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the application */ - break; - case MHD_HTTP_CONFLICT: - { - struct TALER_CoinSpendPublicKeyP coin_pub; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("coin_pub", - &coin_pub), - GNUNET_JSON_spec_end () - }; - const struct DONAU_DenomPublicKey *dki; - bool found = false; - - if (GNUNET_OK != - GNUNET_JSON_parse (j, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - dr->hr.http_status = 0; - dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - for (unsigned int i = 0; i<dh->num_cdds; i++) - { - if (0 != - GNUNET_memcmp (&coin_pub, - &dh->cdds[i].coin_pub)) - continue; - dki = DONAU_get_denomination_key_by_hash (dh->keys, - &dh->cdds[i]. - h_denom_pub); - GNUNET_assert (NULL != dki); - if (GNUNET_OK != - DONAU_check_coin_conflict_ ( - dh->keys, - j, - dki, - &dh->cdds[i].coin_pub, - &dh->cdds[i].coin_sig, - &dh->cdds[i].amount)) - { - GNUNET_break_op (0); - dr->hr.http_status = 0; - dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - found = true; - break; - } - if (! found) - { - GNUNET_break_op (0); - dr->hr.http_status = 0; - dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - dr->hr.ec = TALER_JSON_get_error_code (j); - dr->hr.hint = TALER_JSON_get_error_hint (j); - } - break; - case MHD_HTTP_GONE: - /* could happen if denomination was revoked */ - /* Note: one might want to check /keys for revocation - signature here, alas tricky in case our /keys - is outdated => left to clients */ - dr->hr.ec = TALER_JSON_get_error_code (j); - dr->hr.hint = TALER_JSON_get_error_hint (j); - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - dr->hr.ec = TALER_JSON_get_error_code (j); - dr->hr.hint = TALER_JSON_get_error_hint (j); - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - break; - default: - /* unexpected response code */ - dr->hr.ec = TALER_JSON_get_error_code (j); - dr->hr.hint = TALER_JSON_get_error_hint (j); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for donau deposit\n", - (unsigned int) response_code, - dr->hr.ec); - GNUNET_break_op (0); - break; - } - if (NULL != dh->ai_head) - return; - finish_dh (dh); -} - - -struct DONAU_BatchDepositHandle * -DONAU_batch_deposit ( - struct GNUNET_CURL_Context *ctx, - const char *url, - struct DONAU_Keys *keys, - const struct DONAU_DepositContractDetail *dcd, - unsigned int num_cdds, - const struct DONAU_CoinDepositDetail cdds[static num_cdds], - DONAU_BatchDepositResultCallback cb, - void *cb_cls, - enum TALER_ErrorCode *ec) -{ - struct DONAU_BatchDepositHandle *dh; - json_t *deposit_obj; - json_t *deposits; - CURL *eh; - struct TALER_Amount amount_without_fee; - const struct GNUNET_HashCode *wallet_data_hashp; - - if (GNUNET_TIME_timestamp_cmp (dcd->refund_deadline, - >, - dcd->wire_deadline)) - { - GNUNET_break_op (0); - *ec = TALER_EC_DONAU_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE; - return NULL; - } - dh = GNUNET_new (struct DONAU_BatchDepositHandle); - dh->auditor_chance = AUDITOR_CHANCE; - dh->cb = cb; - dh->cb_cls = cb_cls; - dh->cdds = GNUNET_memdup (cdds, - num_cdds - * sizeof (*cdds)); - dh->num_cdds = num_cdds; - dh->dcd = *dcd; - if (NULL != dcd->policy_details) - TALER_deposit_policy_hash (dcd->policy_details, - &dh->h_policy); - TALER_charity_wire_signature_hash (dcd->charity_payto_uri, - &dcd->wire_salt, - &dh->h_wire); - deposits = json_array (); - GNUNET_assert (NULL != deposits); - for (unsigned int i = 0; i<num_cdds; i++) - { - const struct DONAU_CoinDepositDetail *cdd = &cdds[i]; - const struct DONAU_DenomPublicKey *dki; - const struct TALER_AgeCommitmentHash *h_age_commitmentp; - - dki = DONAU_get_denomination_key_by_hash (keys, - &cdd->h_denom_pub); - if (NULL == dki) - { - *ec = TALER_EC_DONAU_GENERIC_DENOMINATION_KEY_UNKNOWN; - GNUNET_break_op (0); - return NULL; - } - if (0 > - TALER_amount_subtract (&amount_without_fee, - &cdd->amount, - &dki->fees.deposit)) - { - *ec = TALER_EC_DONAU_DEPOSIT_FEE_ABOVE_AMOUNT; - GNUNET_break_op (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Amount: %s\n", - TALER_amount2s (&cdd->amount)); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Fee: %s\n", - TALER_amount2s (&dki->fees.deposit)); - GNUNET_free (dh->cdds); - GNUNET_free (dh); - return NULL; - } - - if (GNUNET_OK != - DONAU_verify_deposit_signature_ (dcd, - &dh->h_policy, - &dh->h_wire, - cdd, - dki)) - { - *ec = TALER_EC_DONAU_DEPOSIT_COIN_SIGNATURE_INVALID; - GNUNET_break_op (0); - GNUNET_free (dh->cdds); - GNUNET_free (dh); - return NULL; - } - if (GNUNET_is_zero (&cdd->h_age_commitment)) - h_age_commitmentp = NULL; - else - h_age_commitmentp = &cdd->h_age_commitment; - GNUNET_assert ( - 0 == - json_array_append_new ( - deposits, - GNUNET_JSON_PACK ( - TALER_JSON_pack_amount ("contribution", - &cdd->amount), - GNUNET_JSON_pack_data_auto ("denom_pub_hash", - &cdd->h_denom_pub), - TALER_JSON_pack_denom_sig ("ub_sig", - &cdd->denom_sig), - GNUNET_JSON_pack_data_auto ("coin_pub", - &cdd->coin_pub), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_data_auto ("h_age_commitment", - h_age_commitmentp)), - GNUNET_JSON_pack_data_auto ("coin_sig", - &cdd->coin_sig) - ))); - } - dh->url = TALER_url_join (url, - "batch-deposit", - NULL); - if (NULL == dh->url) - { - GNUNET_break (0); - *ec = TALER_EC_GENERIC_ALLOCATION_FAILURE; - GNUNET_free (dh->url); - GNUNET_free (dh->cdds); - GNUNET_free (dh); - return NULL; - } - - if (GNUNET_is_zero (&dcd->wallet_data_hash)) - wallet_data_hashp = NULL; - else - wallet_data_hashp = &dcd->wallet_data_hash; - - deposit_obj = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("charity_payto_uri", - dcd->charity_payto_uri), - GNUNET_JSON_pack_data_auto ("wire_salt", - &dcd->wire_salt), - GNUNET_JSON_pack_data_auto ("h_contract_terms", - &dcd->h_contract_terms), - GNUNET_JSON_pack_array_steal ("coins", - deposits), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_data_auto ("wallet_data_hash", - wallet_data_hashp)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_steal ("policy_details", - (json_t *) dcd->policy_details)), - GNUNET_JSON_pack_timestamp ("timestamp", - dcd->wallet_timestamp), - GNUNET_JSON_pack_data_auto ("charity_pub", - &dcd->charity_pub), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_timestamp ("refund_deadline", - dcd->refund_deadline)), - GNUNET_JSON_pack_timestamp ("wire_transfer_deadline", - dcd->wire_deadline)); - GNUNET_assert (NULL != deposit_obj); - eh = DONAU_curl_easy_get_ (dh->url); - if ( (NULL == eh) || - (GNUNET_OK != - TALER_curl_easy_post (&dh->post_ctx, - eh, - deposit_obj)) ) - { - *ec = TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE; - GNUNET_break (0); - if (NULL != eh) - curl_easy_cleanup (eh); - json_decref (deposit_obj); - GNUNET_free (dh->cdds); - GNUNET_free (dh->url); - GNUNET_free (dh); - return NULL; - } - json_decref (deposit_obj); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "URL for deposit: `%s'\n", - dh->url); - dh->ctx = ctx; - dh->keys = DONAU_keys_incref (keys); - dh->job = GNUNET_CURL_job_add2 (ctx, - eh, - dh->post_ctx.headers, - &handle_deposit_finished, - dh); - return dh; -} - - -void -DONAU_batch_deposit_force_dc ( - struct DONAU_BatchDepositHandle *deposit) -{ - deposit->auditor_chance = 1; -} - - -void -DONAU_batch_deposit_cancel ( - struct DONAU_BatchDepositHandle *deposit) -{ - struct TEAH_AuditorInteractionEntry *aie; - - while (NULL != (aie = deposit->ai_head)) - { - GNUNET_assert (aie->dh == deposit); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Not sending deposit confirmation to auditor `%s' due to cancellation\n", - aie->auditor_url); - TALER_AUDITOR_deposit_confirmation_cancel (aie->dch); - GNUNET_CONTAINER_DLL_remove (deposit->ai_head, - deposit->ai_tail, - aie); - GNUNET_free (aie); - } - if (NULL != deposit->job) - { - GNUNET_CURL_job_cancel (deposit->job); - deposit->job = NULL; - } - DONAU_keys_decref (deposit->keys); - GNUNET_free (deposit->url); - GNUNET_free (deposit->cdds); - GNUNET_free (deposit->donau_sigs); - TALER_curl_easy_post_finished (&deposit->post_ctx); - json_decref (deposit->response); - GNUNET_free (deposit); -} - - -/* end of donau_api_batch_deposit.c */ diff --git a/src/lib/donau_api_handle.c b/src/lib/donau_api_handle.c @@ -128,54 +128,3 @@ struct DONAU_GetKeysHandle if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ } while (0) - -/** - * Parse a donau's signing key encoded in JSON. - * - * @param[out] sign_key where to return the result - * @param sign_key_obj json to parse - * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is - * invalid or the @a sign_key_obj is malformed. - */ -static enum GNUNET_GenericReturnValue -parse_json_signkey (struct DONAU_SigningPublicKeyAndValidity *sign_key, - const json_t *sign_key_obj) -{ - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("master_sig", - &sign_key->master_sig), - GNUNET_JSON_spec_fixed_auto ("key", - &sign_key->key), - GNUNET_JSON_spec_timestamp ("stamp_start", - &sign_key->valid_from), - GNUNET_JSON_spec_timestamp ("stamp_expire", - &sign_key->valid_until), - GNUNET_JSON_spec_timestamp ("stamp_end", - &sign_key->valid_legal), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (sign_key_obj, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (! check_sigs) - return GNUNET_OK; - if (GNUNET_OK != - TALER_donau_offline_signkey_validity_verify ( - &sign_key->key, - sign_key->valid_from, - sign_key->valid_until, - sign_key->valid_legal, - master_key, - &sign_key->master_sig)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} diff --git a/src/lib/donau_api_issue_receipts.c b/src/lib/donau_api_issue_receipts.c @@ -69,16 +69,6 @@ struct DONAU_WithdrawHandle void *cb_cls; /** - * Reserve private key. - */ - const struct TALER_ReservePrivateKeyP *reserve_priv; - - /** - * Seed of the planchet. - */ - struct TALER_PlanchetMasterSecretP ps; - - /** * blinding secret */ union TALER_DenominationBlindingKeyP bks; @@ -99,21 +89,11 @@ struct DONAU_WithdrawHandle struct TALER_DonauWithdrawValues alg_values; /** - * Hash of the age commitment for this coin, if applicable. Maybe NULL - */ - const struct TALER_AgeCommitmentHash *ach; - - /** * Denomination key we are withdrawing. */ struct DONAU_DenomPublicKey pk; /** - * Hash of the public key of the coin we are signing. - */ - struct TALER_CoinPubHashP c_hash; - - /** * Handler for the CS R request (only used for TALER_DENOMINATION_CS denominations) */ struct DONAU_CsRWithdrawHandle *csrh; @@ -122,85 +102,6 @@ struct DONAU_WithdrawHandle /** - * Function called when we're done processing the - * HTTP /reserves/$RESERVE_PUB/withdraw request. - * - * @param cls the `struct DONAU_WithdrawHandle` - * @param w2r response data - */ -static void -handle_reserve_withdraw_finished ( - void *cls, - const struct DONAU_Withdraw2Response *w2r) -{ - struct DONAU_WithdrawHandle *wh = cls; - struct DONAU_WithdrawResponse wr = { - .hr = w2r->hr - }; - - wh->wh2 = NULL; - switch (w2r->hr.http_status) - { - case MHD_HTTP_OK: - { - struct TALER_FreshCoin fc; - - if (GNUNET_OK != - TALER_planchet_to_coin (&wh->pk.key, - &w2r->details.ok.blind_sig, - &wh->bks, - &wh->priv, - wh->ach, - &wh->c_hash, - &wh->alg_values, - &fc)) - { - wr.hr.http_status = 0; - wr.hr.ec = TALER_EC_DONAU_WITHDRAW_UNBLIND_FAILURE; - break; - } - wr.details.ok.coin_priv = wh->priv; - wr.details.ok.bks = wh->bks; - wr.details.ok.sig = fc.sig; - wr.details.ok.donau_vals = wh->alg_values; - break; - } - case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ( - "h_payto", - &wr.details.unavailable_for_legal_reasons.h_payto), - GNUNET_JSON_spec_uint64 ( - "requirement_row", - &wr.details.unavailable_for_legal_reasons.requirement_row), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (w2r->hr.reply, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - wr.hr.http_status = 0; - wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; - break; - } - } - break; - default: - break; - } - wh->cb (wh->cb_cls, - &wr); - if (MHD_HTTP_OK == w2r->hr.http_status) - TALER_denom_sig_free (&wr.details.ok.sig); - DONAU_withdraw_cancel (wh); -} - - -/** * Function called when stage 1 of CS withdraw is finished (request r_pub's) * * @param cls the `struct DONAU_WithdrawHandle` diff --git a/src/testing/testing_api_misc.c b/src/testing/testing_api_misc.c @@ -226,12 +226,10 @@ TALER_TESTING_cleanup_files_cfg ( (void) cls; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, - "donau-offline", "SECM_TOFU_FILE", &dir)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "donau-offline", "SECM_TOFU_FILE"); return GNUNET_SYSERR; }