donau

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

commit ff4fe2e23b0f82bc4108224072241243802c8d7c
parent 9d892401d816baa2c69312d636d2310ec9ae405f
Author: Christian Grothoff <grothoff@gnu.org>
Date:   Wed,  8 Apr 2026 02:57:50 +0200

rename donau handlers for consistency

Diffstat:
Msrc/donau/Makefile.am | 23++++++++++++-----------
Msrc/donau/donau-httpd.c | 42+++++++++++++++++++++++-------------------
Msrc/donau/donau-httpd.h | 1+
Dsrc/donau/donau-httpd_batch-issue.c | 468-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_batch-issue.h | 41-----------------------------------------
Dsrc/donau/donau-httpd_batch-submit.c | 255-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_batch-submit.h | 41-----------------------------------------
Dsrc/donau/donau-httpd_charities_get.c | 119-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_charity.h | 97-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_charity_delete.c | 83-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_charity_get.c | 122-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_charity_insert.c | 113-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_charity_patch.c | 162-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_config.c | 55-------------------------------------------------------
Dsrc/donau/donau-httpd_config.h | 58----------------------------------------------------------
Dsrc/donau/donau-httpd_csr.c | 126-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_csr.h | 42------------------------------------------
Asrc/donau/donau-httpd_delete-charities-CHARITY_ID.c | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_delete-charities-CHARITY_ID.h | 40++++++++++++++++++++++++++++++++++++++++
Dsrc/donau/donau-httpd_donation-statement.c | 131-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_donation-statement.h | 41-----------------------------------------
Asrc/donau/donau-httpd_get-charities.c | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_get-charities.h | 38++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_get-charity-CHARITY_ID.c | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_get-charity-CHARITY_ID.h | 44++++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_get-config.c | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_get-config.h | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_get-donation-statement-YEAR-HASH_DONOR_ID.c | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_get-donation-statement-YEAR-HASH_DONOR_ID.h | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_get-history.c | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_get-history.h | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_get-keys.c | 1454+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_get-keys.h | 207+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/donau/donau-httpd_history.h | 51---------------------------------------------------
Dsrc/donau/donau-httpd_history_get.c | 121-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_keys.c | 1454-------------------------------------------------------------------------------
Dsrc/donau/donau-httpd_keys.h | 207-------------------------------------------------------------------------------
Asrc/donau/donau-httpd_patch-charities-CHARITY_ID.c | 162+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_patch-charities-CHARITY_ID.h | 42++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_post-batch-issue-CHARITY_ID.c | 468+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_post-batch-issue-CHARITY_ID.h | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_post-batch-submit.c | 255+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_post-batch-submit.h | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_post-charities.c | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_post-charities.h | 42++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_post-csr-issue.c | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/donau/donau-httpd_post-csr-issue.h | 42++++++++++++++++++++++++++++++++++++++++++
47 files changed, 3934 insertions(+), 3817 deletions(-)

diff --git a/src/donau/Makefile.am b/src/donau/Makefile.am @@ -40,18 +40,19 @@ donau_httpd_LDADD = \ donau_httpd_SOURCES = \ donau-httpd.c donau-httpd.h \ donau-httpd_db.c donau-httpd_db.h \ - donau-httpd_keys.c donau-httpd_keys.h \ - donau-httpd_config.c donau-httpd_config.h \ - donau-httpd_charities_get.c donau-httpd_charity.h \ - donau-httpd_charity_delete.c \ - donau-httpd_charity_get.c donau-httpd_charity_insert.c \ - donau-httpd_charity_patch.c \ - donau-httpd_history_get.c \ - donau-httpd_donation-statement.c donau-httpd_donation-statement.h \ - donau-httpd_batch-submit.c donau-httpd_batch-submit.h \ + donau-httpd_get-keys.c donau-httpd_get-keys.h \ + donau-httpd_get-config.c donau-httpd_get-config.h \ + donau-httpd_get-charities.c donau-httpd_get-charities.h \ + donau-httpd_delete-charities-CHARITY_ID.c donau-httpd_delete-charities-CHARITY_ID.h \ + donau-httpd_get-charity-CHARITY_ID.c donau-httpd_get-charity-CHARITY_ID.h \ + donau-httpd_post-charities.c donau-httpd_post-charities.h \ + donau-httpd_patch-charities-CHARITY_ID.c donau-httpd_patch-charities-CHARITY_ID.h \ + donau-httpd_get-history.c donau-httpd_get-history.h \ + donau-httpd_get-donation-statement-YEAR-HASH_DONOR_ID.c donau-httpd_get-donation-statement-YEAR-HASH_DONOR_ID.h \ + donau-httpd_post-batch-submit.c donau-httpd_post-batch-submit.h \ donau-httpd_terms.c donau-httpd_terms.h \ - donau-httpd_csr.c donau-httpd_csr.h \ - donau-httpd_batch-issue.c donau-httpd_batch-issue.h + donau-httpd_post-csr-issue.c donau-httpd_post-csr-issue.h \ + donau-httpd_post-batch-issue-CHARITY_ID.c donau-httpd_post-batch-issue-CHARITY_ID.h # Testcases diff --git a/src/donau/donau-httpd.c b/src/donau/donau-httpd.c @@ -29,14 +29,18 @@ #include "donaudb_lib.h" #include "donau_util.h" #include "donau_json_lib.h" -#include "donau-httpd_config.h" -#include "donau-httpd_keys.h" -#include "donau-httpd_charity.h" -#include "donau-httpd_donation-statement.h" -#include "donau-httpd_batch-issue.h" -#include "donau-httpd_batch-submit.h" -#include "donau-httpd_history.h" -#include "donau-httpd_csr.h" +#include "donau-httpd_get-config.h" +#include "donau-httpd_get-keys.h" +#include "donau-httpd_get-charities.h" +#include "donau-httpd_get-charity-CHARITY_ID.h" +#include "donau-httpd_post-charities.h" +#include "donau-httpd_patch-charities-CHARITY_ID.h" +#include "donau-httpd_delete-charities-CHARITY_ID.h" +#include "donau-httpd_get-donation-statement-YEAR-HASH_DONOR_ID.h" +#include "donau-httpd_post-batch-issue-CHARITY_ID.h" +#include "donau-httpd_post-batch-submit.h" +#include "donau-httpd_get-history.h" +#include "donau-httpd_post-csr-issue.h" #include "donau-httpd_terms.h" #include "donaudb_plugin.h" #include <gnunet/gnunet_mhd_compat.h> @@ -419,7 +423,7 @@ handle_get_charities (struct DH_RequestContext *rc, const char *const args[]) { GNUNET_break (NULL == args[0]); - return DH_handler_charities_get (rc); + return DH_handler_get_charities (rc); } @@ -442,7 +446,7 @@ handle_get_charity_id (struct DH_RequestContext *rc, DONAU_HTTP_HEADER_CHARITY_SIGNATURE, &charity_sig, sig_required); - return DH_handler_charity_get (rc, + return DH_handler_get_charity (rc, &charity_sig, args[0]); } @@ -488,13 +492,13 @@ handle_mhd_request (void *cls, { .url = "config", .method = MHD_HTTP_METHOD_GET, - .handler.get = &DH_handler_config + .handler.get = &DH_handler_get_config }, /* GET keys endpoints (we only really have "/keys") */ { .url = "keys", .method = MHD_HTTP_METHOD_GET, - .handler.get = &DH_handler_keys + .handler.get = &DH_handler_get_keys }, /* GET charities */ { @@ -514,14 +518,14 @@ handle_mhd_request (void *cls, { .url = "charities", .method = MHD_HTTP_METHOD_POST, - .handler.post = &DH_handler_charity_post, + .handler.post = &DH_handler_post_charities, .needs_authorization = true }, /* PATCH charities */ { .url = "charities", .method = MHD_HTTP_METHOD_PATCH, - .handler.patch = &DH_handler_charity_patch, + .handler.patch = &DH_handler_patch_charities, .nargs = 1, .needs_authorization = true }, @@ -529,7 +533,7 @@ handle_mhd_request (void *cls, { .url = "charities", .method = MHD_HTTP_METHOD_DELETE, - .handler.delete = &DH_handler_charity_delete, + .handler.delete = &DH_handler_delete_charities, .nargs = 1, .needs_authorization = true }, @@ -537,27 +541,27 @@ handle_mhd_request (void *cls, { .url = "csr-issue", .method = MHD_HTTP_METHOD_POST, - .handler.post = &DH_handler_csr_issue, + .handler.post = &DH_handler_post_csr_issue, .nargs = 0 }, /* POST batch issue receipts */ { .url = "batch-issue", .method = MHD_HTTP_METHOD_POST, - .handler.post = &DH_handler_issue_receipts_post, + .handler.post = &DH_handler_post_batch_issue, .nargs = 1 }, /* POST submitted receipts */ { .url = "batch-submit", .method = MHD_HTTP_METHOD_POST, - .handler.post = &DH_handler_submit_receipts_post + .handler.post = &DH_handler_post_batch_submit }, /* GET donation statement */ { .url = "donation-statement", .method = MHD_HTTP_METHOD_GET, - .handler.get = &DH_handler_donation_statement_get, + .handler.get = &DH_handler_get_donation_statement, .nargs = 2, .nargs_is_upper_bound = true }, diff --git a/src/donau/donau-httpd.h b/src/donau/donau-httpd.h @@ -27,6 +27,7 @@ #include <taler/taler_json_lib.h> #include <taler/taler_util.h> #include <gnunet/gnunet_mhd_compat.h> +#include "donaudb_plugin.h" /** diff --git a/src/donau/donau-httpd_batch-issue.c b/src/donau/donau-httpd_batch-issue.c @@ -1,468 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2024, 2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_batch-issue.c - * @brief Handle request to issue receipts. - * @author Lukas Matyja - */ -#include <donau_config.h> -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include <pthread.h> -#include <taler/taler_json_lib.h> -#include <taler/taler_mhd_lib.h> -#include <taler/taler_signatures.h> -#include "donaudb_plugin.h" -#include "donau-httpd_batch-issue.h" -#include "donau-httpd_db.h" -#include "donau_json_lib.h" -#include "donau-httpd_keys.h" - - -/** - * Parse a bkp encoded in JSON. - * - * @param[out] bkp where to return the result - * @param bkp_key_obj json to parse - * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if @a bkp_key_obj - * is malformed. - */ -static enum GNUNET_GenericReturnValue -parse_json_bkp (struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkp, - const json_t *bkp_key_obj) -{ - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("h_donation_unit_pub", - &bkp->h_donation_unit_pub), - DONAU_JSON_spec_blinded_donation_identifier ("blinded_udi", - &bkp->blinded_udi), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (bkp_key_obj, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - /* FIXME: Check for duplicate blinded UDIs.*/ - return GNUNET_OK; -} - - -/** - * Free @a bkps array. - * - * @param num_bkps length of the array - * @param[in] bkps array to release - */ -static void -free_bkps (size_t num_bkps, - struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps) -{ - for (unsigned int i = 0; i<num_bkps; i++) - { - struct DONAU_BlindedUniqueDonorIdentifier *budi = &bkps[i].blinded_udi; - - if (NULL != budi->blinded_message) - GNUNET_CRYPTO_blinded_message_decref (budi->blinded_message); - } - GNUNET_free (bkps); -} - - -/** - * Parse signatures to JSON. - * - * @param num_sig number of signatures - * @param signatures Blinded donation unit signatures - * @param[out] j_signatures JSON object - * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if we could not parse - * is malformed. - */ -static void -signatures_to_json (const size_t num_sig, - struct DONAU_BlindedDonationUnitSignature *signatures, - json_t *j_signatures) -{ - for (size_t i = 0; i < num_sig; i++) - { - struct DONAU_BlindedDonationUnitSignature *signature = &signatures[i]; - - GNUNET_assert ( - 0 == json_array_append_new ( - j_signatures, - GNUNET_JSON_PACK ( - DONAU_JSON_pack_blinded_donation_unit_sig ("blinded_signature", - signature)))); - } -} - - -MHD_RESULT -DH_handler_issue_receipts_post (struct DH_RequestContext *rc, - const json_t *root, - const char *const args[1]) -{ - struct DONAU_CharitySignatureP charity_sig; - uint64_t year; - bool second_time = false; - unsigned long long charity_id; - char dummy; - const json_t *budikeypairs; - size_t num_bkps; - struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps; - struct DONAUDB_CharityMetaData charity_meta; - json_t *blind_signatures; - struct DONAU_DonationReceiptHashP h_receipts; - struct TALER_Amount receipts_sum; - - if ( (NULL == args[0]) || - (1 != sscanf (args[0], - "%llu%c", - &charity_id, - &dummy)) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "charity_id"); - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "issue receipts for charity id: %llu\n", - charity_id); - - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("budikeypairs", - &budikeypairs), - GNUNET_JSON_spec_fixed_auto ("charity_sig", - &charity_sig), - GNUNET_JSON_spec_uint64 ("year", - &year), - GNUNET_JSON_spec_end () - }; - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (rc->connection, - root, - spec); - if (GNUNET_SYSERR == res) - return MHD_NO; /* hard failure */ - if (GNUNET_NO == res) - { - GNUNET_break_op (0); - return MHD_YES; /* failure */ - } - } - - /* parse the budikeypairs array */ - num_bkps = json_array_size (budikeypairs); - if (0 == num_bkps) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "budikeypairs"); - - } - - bkps = GNUNET_new_array (num_bkps, - struct DONAU_BlindedUniqueDonorIdentifierKeyPair); - { - json_t *bkp_obj; - size_t index; - - json_array_foreach (budikeypairs, - index, - bkp_obj) - { - if (GNUNET_SYSERR == - parse_json_bkp (&bkps[index], - bkp_obj)) - { - GNUNET_break_op (0); - free_bkps (num_bkps, - bkps); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "budikeypairs"); - } - } - } - - { - enum GNUNET_DB_QueryStatus qs_charity; - - qs_charity = DH_plugin->lookup_charity (DH_plugin->cls, - charity_id, - &charity_meta); - switch (qs_charity) - { - case GNUNET_DB_STATUS_HARD_ERROR: - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break_op (0); - free_bkps (num_bkps, - bkps); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_break_op (0); - free_bkps (num_bkps, - bkps); - return TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_DONAU_CHARITY_NOT_FOUND, - NULL); - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - } - - /* verify charity signature */ - if (GNUNET_OK != - DONAU_charity_bkp_verify (num_bkps, - bkps, - &charity_meta.charity_pub, - &charity_sig)) - { - GNUNET_break_op (0); - free_bkps (num_bkps, - bkps); - GNUNET_free (charity_meta.charity_name); - GNUNET_free (charity_meta.charity_url); - return TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_DONAU_CHARITY_SIGNATURE_INVALID, - "charity_sig"); - } - - GNUNET_free (charity_meta.charity_name); - GNUNET_free (charity_meta.charity_url); - { - /* request already made? -> idempotent */ - enum GNUNET_DB_QueryStatus qs_check_receipts; - struct DONAUDB_IssuedReceiptsMetaData check_receipts_meta; - struct GNUNET_HashContext *hc; - - blind_signatures = json_array (); - GNUNET_assert (NULL != blind_signatures); - hc = GNUNET_CRYPTO_hash_context_start (); - for (size_t i = 0; i < num_bkps; i++) - { - const struct GNUNET_CRYPTO_BlindedMessage *bm - = bkps[i].blinded_udi.blinded_message; - - GNUNET_CRYPTO_hash_context_read (hc, - &bkps[i].h_donation_unit_pub, - sizeof (bkps[i].h_donation_unit_pub)); - switch (bm->cipher) - { - case GNUNET_CRYPTO_BSA_INVALID: - GNUNET_assert (0); - break; - case GNUNET_CRYPTO_BSA_CS: - GNUNET_CRYPTO_hash_context_read ( - hc, - &bm->details.cs_blinded_message, - sizeof (bm->details.cs_blinded_message)); - break; - case GNUNET_CRYPTO_BSA_RSA: - GNUNET_CRYPTO_hash_context_read ( - hc, - bm->details.rsa_blinded_message.blinded_msg, - bm->details.rsa_blinded_message.blinded_msg_size); - break; - } - } - GNUNET_CRYPTO_hash_context_read (hc, - &charity_sig, - sizeof (struct DONAU_CharitySignatureP)); - GNUNET_CRYPTO_hash_context_read (hc, - &year, - sizeof (uint64_t)); - GNUNET_CRYPTO_hash_context_finish (hc, - &h_receipts.hash); - -start: - qs_check_receipts - = DH_plugin->lookup_issued_receipts (DH_plugin->cls, - &h_receipts, - &check_receipts_meta); - switch (qs_check_receipts) - { - case GNUNET_DB_STATUS_HARD_ERROR: - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - free_bkps (num_bkps, - bkps); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "request has not been made yet (first time)!\n"); - break; /* it's the first request from the charity, we can proceed */ - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "request has been made already!\n"); - signatures_to_json (num_bkps, - check_receipts_meta.blinded_sigs, - blind_signatures); - for (size_t i = 0; i < check_receipts_meta.num_sig; i++) - { - GNUNET_CRYPTO_blinded_sig_decref ( - check_receipts_meta.blinded_sigs[i].blinded_sig); - } - free_bkps (num_bkps, - bkps); - return TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - TALER_JSON_pack_amount ("issued_amount", - &check_receipts_meta.amount), - GNUNET_JSON_pack_array_steal ("blind_signatures", - blind_signatures)); - } - } - - /* calculate the sum of all receipts */ - - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (DH_currency, - &receipts_sum)); - for (size_t i = 0; i < num_bkps; i++) - { - struct DH_DonationUnitKey *dk; - - if (NULL == (dk = DH_keys_donation_unit_by_hash ( - &bkps[i].h_donation_unit_pub))) - { - GNUNET_break_op (0); - free_bkps (num_bkps, - bkps); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_DONAU_GENERIC_KEYS_MISSING, - NULL); - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "public key value: %lu.%u\n", - dk->value.value, - dk->value.fraction); - GNUNET_assert (0 <= TALER_amount_add (&receipts_sum, - &receipts_sum, - &dk->value)); - } - - /* sign budis and send the signatures back */ - { - struct DONAU_BlindedDonationUnitSignature du_sigs[num_bkps]; - struct DONAU_BkpSignData bkps_sign_data[num_bkps]; - enum TALER_ErrorCode batch_sign_ec; - enum GNUNET_DB_QueryStatus qs_insert_ir; - bool smaller_than_max_per_year = false; - - for (size_t i = 0; i < num_bkps; i++) - { - bkps_sign_data[i].h_donation_unit_pub = &bkps[i].h_donation_unit_pub; - bkps_sign_data[i].budi = &bkps[i].blinded_udi; - } - batch_sign_ec = DH_keys_donation_unit_batch_sign (num_bkps, - bkps_sign_data, - du_sigs); - if (TALER_EC_NONE != batch_sign_ec) - { - GNUNET_break_op (0); - free_bkps (num_bkps, - bkps); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - batch_sign_ec, - NULL); - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "made blind signatures!\n"); - free_bkps (num_bkps, - bkps); - - /* save new receipts to date and save receipts Request (blinded signatures, - * charity id, amount, hash over bkps) to make it idempotent*/ - qs_insert_ir = DH_plugin->insert_issued_receipt ( - DH_plugin->cls, - num_bkps, - du_sigs, - (uint64_t) charity_id, - &h_receipts, - &receipts_sum, - &smaller_than_max_per_year); - switch (qs_insert_ir) - { - case GNUNET_DB_STATUS_HARD_ERROR: - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_assert (! second_time); - second_time = true; - goto start; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - if (! smaller_than_max_per_year) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_DONAU_EXCEEDING_DONATION_LIMIT, - NULL); - } - break; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "issue receipts request is saved! (idempotent)\n"); - - signatures_to_json (num_bkps, - du_sigs, - blind_signatures); - for (unsigned int i = 0; i<num_bkps; i++) - GNUNET_CRYPTO_blinded_sig_decref (du_sigs[i].blinded_sig); - } - return TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - TALER_JSON_pack_amount ("issued_amount", - &receipts_sum), - GNUNET_JSON_pack_array_steal ("blind_signatures", - blind_signatures)); -} - - -/* end of donau-httpd_batch-issue.c */ diff --git a/src/donau/donau-httpd_batch-issue.h b/src/donau/donau-httpd_batch-issue.h @@ -1,41 +0,0 @@ -/* - This file is part of TALER - 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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_batch-issue.h - * @brief Handle /batch-issue requests - * @author Lukas Matyja - */ -#ifndef DONAU_HTTPD_BATCH_ISSUE_H -#define DONAU_HTTPD_BATCH_ISSUE_H - -#include <microhttpd.h> -#include "donau-httpd.h" - - -/** - * Handle a POST "/batch-issue" request. - * - * @param connection the MHD connection to handle - * @param root uploaded JSON data - * @return MHD result code - */ -MHD_RESULT -DH_handler_issue_receipts_post ( - struct DH_RequestContext *rc, - const json_t *root, - const char *const args[1]); - -#endif diff --git a/src/donau/donau-httpd_batch-submit.c b/src/donau/donau-httpd_batch-submit.c @@ -1,255 +0,0 @@ -/* - This file is part of TALER - 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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_batch-submit.c - * @brief Handle request to insert a submitted receipt. - * @author Johannes Casaburi - */ -#include "donau_config.h" -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include <pthread.h> -#include "taler/taler_json_lib.h" -#include "taler/taler_mhd_lib.h" -#include "taler/taler_signatures.h" -#include "donaudb_plugin.h" -#include "donau-httpd_batch-submit.h" -#include "donau-httpd_keys.h" - - -/** - * Closure for #insert_submitted_receipts() - */ -struct InsertReceiptContext -{ - struct DONAU_HashDonorTaxId h_donor_tax_id; - struct DONAU_DonationReceipt *donation_receipts; - size_t num_dr; - uint64_t donation_year; -}; - -/** - * Parse a donation receipt encoded in JSON. - * - * @param[out] dr where to return the result - * @param dr_obj json to parse - * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if @a dr_obj - * is malformed. - */ -static enum GNUNET_GenericReturnValue -parse_json_dr (struct DONAU_DonationReceipt *dr, - const json_t *dr_obj) -{ - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("h_donation_unit_pub", - &dr->h_donation_unit_pub), - GNUNET_JSON_spec_fixed_auto ("nonce", - &dr->nonce), - GNUNET_JSON_spec_unblinded_signature ("donation_unit_sig", - &dr->donation_unit_sig.unblinded_sig), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (dr_obj, - spec, - NULL, - NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - return GNUNET_OK; -} - - -/** - * Free data in @a irc, but not @a irc itself - * - * @param[in,out] irc data structure to clean up - */ -static void -free_irc (struct InsertReceiptContext *irc) -{ - for (size_t i = 0; i<irc->num_dr; i++) - GNUNET_CRYPTO_unblinded_sig_decref (irc->donation_receipts[i]. - donation_unit_sig.unblinded_sig); - GNUNET_free (irc->donation_receipts); -} - - -MHD_RESULT -DH_handler_submit_receipts_post (struct DH_RequestContext *rc, - const json_t *root, - const char *const args[]) -{ - struct InsertReceiptContext irc = {0}; - const json_t *donation_receipts; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("h_donor_tax_id", - &irc.h_donor_tax_id), - GNUNET_JSON_spec_array_const ("donation_receipts", - &donation_receipts), - GNUNET_JSON_spec_uint64 ("donation_year", - &irc.donation_year), - GNUNET_JSON_spec_end () - }; - size_t num_dr; - - (void) args; - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (rc->connection, - root, - spec); - if (GNUNET_SYSERR == res) - return MHD_NO; /* hard failure */ - if (GNUNET_NO == res) - { - GNUNET_break_op (0); - return MHD_YES; /* failure */ - } - } - - /* parse the donation receipts */ - num_dr = json_array_size (donation_receipts); - - if (0 == num_dr) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "donation_receipts"); - } - { - json_t *dr_obj; - size_t index; - - irc.num_dr = num_dr; - irc.donation_receipts = GNUNET_new_array (num_dr, - struct DONAU_DonationReceipt); - - json_array_foreach (donation_receipts, - index, - dr_obj) - { - if (GNUNET_SYSERR == - parse_json_dr (&irc.donation_receipts[index], - dr_obj)) - { - GNUNET_break_op (0); - free_irc (&irc); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "donation_receipts"); - } - } - } - - for (size_t i = 0; i < num_dr; i++) - { - struct DONAU_UniqueDonorIdentifierHashP udi_hash; - struct DH_DonationUnitKey *dk; - - /* Check nonce unique*/ - for (size_t j = i + 1; j < num_dr; j++) - { - if (0 == - GNUNET_memcmp (&irc.donation_receipts[i].nonce, - &irc.donation_receipts[j].nonce)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Donation receipt nonce is not unique!\n"); - free_irc (&irc); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_CONFLICT, - TALER_EC_DONAU_DONOR_IDENTIFIER_NONCE_REUSE, - NULL); - } - } - - /* Check if donation unit exists*/ - if (NULL == (dk = DH_keys_donation_unit_by_hash ( - &irc.donation_receipts[i].h_donation_unit_pub))) - { - GNUNET_break_op (0); - free_irc (&irc); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_DONAU_GENERIC_DONATION_UNIT_UNKNOWN, - NULL); - } - - DONAU_unique_donor_id_hash ( - &irc.h_donor_tax_id, - &irc.donation_receipts[i].nonce, - &udi_hash); - - /* Check signature*/ - if (GNUNET_OK != DONAU_donation_receipt_verify ( - &dk->donation_unit_pub, - &udi_hash, - &irc.donation_receipts[i].donation_unit_sig)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Donation receipt signature invalid!\n"); - free_irc (&irc); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_DONAU_DONATION_RECEIPT_SIGNATURE_INVALID, - NULL); - - } - } - - { - enum GNUNET_DB_QueryStatus qs; - - qs = DH_plugin->insert_submitted_receipts ( - DH_plugin->cls, - &irc.h_donor_tax_id, - num_dr, - irc.donation_receipts, - irc.donation_year); - free_irc (&irc); - if (qs < 0) - { - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - } - } - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "submitted receipts inserted!\n"); - - return TALER_MHD_reply_static ( - rc->connection, - MHD_HTTP_CREATED, - NULL, - NULL, - 0); -} - - -/* end of donau-httpd_batch-submit.c */ diff --git a/src/donau/donau-httpd_batch-submit.h b/src/donau/donau-httpd_batch-submit.h @@ -1,41 +0,0 @@ -/* - This file is part of TALER - 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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_batch-submit.h - * @brief Handle /submit requests - * @author Johannes Casaburi - */ -#ifndef DONAU_HTTPD_BATCH_SUBMIT_H -#define DONAU_HTTPD_BATCH_SUBMIT_H - -#include <microhttpd.h> -#include "donau-httpd.h" - - -/** - * Handle a POST "/submit" request. - * - * @param connection the MHD connection to handle - * @param root uploaded JSON data - * @return MHD result code - */ -MHD_RESULT -DH_handler_submit_receipts_post ( - struct DH_RequestContext *rc, - const json_t *root, - const char *const args[]); - -#endif diff --git a/src/donau/donau-httpd_charities_get.c b/src/donau/donau-httpd_charities_get.c @@ -1,119 +0,0 @@ -/* - This file is part of TALER - 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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_charities_get.c - * @brief Return charities - * @author Johannes Casaburi - */ -#include <donau_config.h> -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include <taler/taler_json_lib.h> -#include <taler/taler_mhd_lib.h> -#include <taler/taler_signatures.h> -#include "donau-httpd.h" -#include "donaudb_plugin.h" -#include "donau-httpd_charity.h" - - -/** - * Maximum number of charities we return per request. - */ -#define MAX_RECORDS 1024 - -/** - * Return charities information. - * - * @param cls closure - */ -static enum GNUNET_GenericReturnValue -charities_cb ( - void *cls, - uint64_t charity_id, - const struct DONAU_CharityPublicKeyP *charity_pub, - const char *charity_name, - const struct TALER_Amount *max_per_year, - uint32_t current_year, - const struct TALER_Amount *receipts_to_date) -{ - json_t *charities = cls; - - GNUNET_assert ( - 0 == - json_array_append_new ( - charities, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_uint64 ("charity_id", - charity_id), - GNUNET_JSON_pack_data_auto ("charity_pub", - charity_pub), - GNUNET_JSON_pack_string ("charity_name", - charity_name), - TALER_JSON_pack_amount ("max_per_year", - max_per_year), - GNUNET_JSON_pack_uint64 ("current_year", - current_year), - TALER_JSON_pack_amount ("receipts_to_date", - receipts_to_date)))); - return GNUNET_OK; -} - - -MHD_RESULT -DH_handler_charities_get ( - struct DH_RequestContext *rc) -{ - - { - json_t *charities; - enum GNUNET_DB_QueryStatus qs; - - charities = json_array (); - GNUNET_assert (NULL != charities); - qs = DH_plugin->get_charities (DH_plugin->cls, - &charities_cb, - charities); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - case GNUNET_DB_STATUS_SOFT_ERROR: - json_decref (charities); - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return TALER_MHD_reply_static ( - rc->connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - return TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - GNUNET_JSON_pack_array_steal ("charities", - charities)); - } -} - - -/* end of donau-httpd_charities_get.c */ diff --git a/src/donau/donau-httpd_charity.h b/src/donau/donau-httpd_charity.h @@ -1,97 +0,0 @@ -/* - This file is part of TALER - 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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_charity.h - * @brief Handle /charity requests - * @author Johannes Casaburi - */ -#ifndef DONAU_HTTPD_CHARITY_H -#define DONAU_HTTPD_CHARITY_H - -#include <microhttpd.h> -#include "donau-httpd.h" -#include "donaudb_plugin.h" - - -/** - * Handle a POST "/charity" request. - * - * @param connection the MHD connection to handle - * @param root uploaded JSON data - * @return MHD result code - */ -MHD_RESULT -DH_handler_charity_post ( - struct DH_RequestContext *rc, - const json_t *root, - const char *const args[]); - - -/** - * Handle a PATCH "/charities/$CHARITY_ID" request. - * - * @param rc request context - * @param root uploaded JSON data with updates - * @param args array with the charity identifier in args[0] - * @return MHD result code - */ -MHD_RESULT -DH_handler_charity_patch ( - struct DH_RequestContext *rc, - const json_t *root, - const char *const args[]); - - -/** - * Handle a GET "/charities/$CHARITY_ID" request. - * - * @param rc request context - * @param charity_sig signature of the charity authorizing access - * (to be checked!) - * @param charity_id_s the "$CHARITY_ID" argument - * @return MHD result code - */ -MHD_RESULT -DH_handler_charity_get ( - struct DH_RequestContext *rc, - const struct DONAU_CharitySignatureP *charity_sig, - const char *charity_id_s); - - -/** - * Handle a GET "/charities" request. - * - * @param rc request context - * @param args GET arguments (should be one) - * @return MHD result code - */ -MHD_RESULT -DH_handler_charities_get ( - struct DH_RequestContext *rc); - -/** - * Handle a DELETE "/charity/$CHARITY_ID" request. - * - * @param rc request details about the request to handle - * @param args argument with the public key of the purse - * @return MHD result code - */ -MHD_RESULT -DH_handler_charity_delete ( - struct DH_RequestContext *rc, - const char *const args[1]); - -#endif diff --git a/src/donau/donau-httpd_charity_delete.c b/src/donau/donau-httpd_charity_delete.c @@ -1,83 +0,0 @@ -/* - This file is part of TALER - 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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_charity_delete.c - * @brief Handle DELETE /charitys/$CHARITY_ID requests. - * @author Johannes Casaburi - */ -#include "donau_config.h" -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include "taler/taler_dbevents.h" -#include "taler/taler_json_lib.h" -#include "taler/taler_mhd_lib.h" -#include "donau-httpd_charity.h" - -MHD_RESULT -DH_handler_charity_delete ( - struct DH_RequestContext *rc, - const char *const args[1]) -{ - unsigned long long charity_id; - char dummy; - - if ( (NULL == args[0]) || - (1 != sscanf (args[0], - "%llu%c", - &charity_id, - &dummy)) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "charity_id"); - } - - { - enum GNUNET_DB_QueryStatus qs; - - qs = DH_plugin->do_charity_delete (DH_plugin->cls, - (uint64_t) charity_id); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return TALER_MHD_reply_static ( - rc->connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); - } - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - } -} - - -/* end of donau-httpd_charity_delete.c */ diff --git a/src/donau/donau-httpd_charity_get.c b/src/donau/donau-httpd_charity_get.c @@ -1,122 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023-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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_charity_get.c - * @brief Return summary information about a charity - * @author Johannes Casaburi - */ -#include <donau_config.h> -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include <pthread.h> -#include <taler/taler_json_lib.h> -#include <taler/taler_mhd_lib.h> -#include <taler/taler_signatures.h> -#include "donaudb_plugin.h" -#include "donau-httpd_charity.h" - - -/** - * Maximum number of records we return per request. - */ -#define MAX_RECORDS 1024 - - -MHD_RESULT -DH_handler_charity_get ( - struct DH_RequestContext *rc, - const struct DONAU_CharitySignatureP *charity_sig, - const char *charity_id_s) -{ - unsigned long long charity_id; - char dummy; - - if ( (NULL == charity_id_s) || - (1 != sscanf (charity_id_s, - "%llu%c", - &charity_id, - &dummy)) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "charity_id"); - } - - { - struct DONAUDB_CharityMetaData meta; - enum GNUNET_DB_QueryStatus qs; - MHD_RESULT result; - - qs = DH_plugin->lookup_charity (DH_plugin->cls, - (uint64_t) charity_id, - &meta); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_DONAU_CHARITY_NOT_FOUND, - charity_id_s); - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - - if (GNUNET_OK != - DONAU_charity_get_info_verify (&meta.charity_pub, - charity_sig)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_GENERIC_FORBIDDEN, - DONAU_HTTP_HEADER_CHARITY_SIGNATURE); - } - result = TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - GNUNET_JSON_pack_data_auto ("charity_pub", - &meta.charity_pub), - GNUNET_JSON_pack_string ("url", - meta.charity_url), - GNUNET_JSON_pack_string ("name", - meta.charity_name), - TALER_JSON_pack_amount ("max_per_year", - &meta.max_per_year), - TALER_JSON_pack_amount ("receipts_to_date", - &meta.receipts_to_date), - GNUNET_JSON_pack_uint64 ("current_year", - meta.current_year)); - - GNUNET_free (meta.charity_url); - GNUNET_free (meta.charity_name); - return result; - } -} - - -/* end of donau-httpd_charity_get.c */ diff --git a/src/donau/donau-httpd_charity_insert.c b/src/donau/donau-httpd_charity_insert.c @@ -1,113 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2024, 2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_charity_insert.c - * @brief Handle request to insert a charity. - * @author Johannes Casaburi - * @author Christian Grothoff - */ -#include <donau_config.h> -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include <pthread.h> -#include <taler/taler_json_lib.h> -#include <taler/taler_mhd_lib.h> -#include <taler/taler_signatures.h> -#include "donaudb_plugin.h" -#include "donau-httpd_charity.h" -#include "donau-httpd_db.h" - - -MHD_RESULT -DH_handler_charity_post (struct DH_RequestContext *rc, - const json_t *root, - const char *const args[]) -{ - struct DONAU_CharityPublicKeyP charity_pub; - const char *charity_name; - const char *charity_url; - struct TALER_Amount max_per_year; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("charity_pub", - &charity_pub), - GNUNET_JSON_spec_string ("charity_name", - &charity_name), - GNUNET_JSON_spec_string ("charity_url", - &charity_url), - TALER_JSON_spec_amount ("max_per_year", - DH_currency, - &max_per_year), - GNUNET_JSON_spec_end () - }; - enum GNUNET_DB_QueryStatus qs; - uint64_t charity_id; - - (void) args; - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (rc->connection, - root, - spec); - if (GNUNET_SYSERR == res) - return MHD_NO; /* hard failure */ - if (GNUNET_NO == res) - { - GNUNET_break_op (0); - return MHD_YES; /* failure */ - } - } - qs = DH_plugin->insert_charity (DH_plugin->cls, - &charity_pub, - charity_name, - charity_url, - &max_per_year, - &charity_id); - switch (qs) - { - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_charity"); - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_charity"); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_CONFLICT, - TALER_EC_DONAU_CHARITY_PUB_EXISTS, - NULL); - return GNUNET_DB_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - return TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_CREATED, - GNUNET_JSON_pack_uint64 ("charity_id", - charity_id)); -} - - -/* end of donau-httpd_charity_insert.c */ diff --git a/src/donau/donau-httpd_charity_patch.c b/src/donau/donau-httpd_charity_patch.c @@ -1,162 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_charity_patch.c - * @brief Handle request to update a charity entry. - * @author Bohdan Potuzhnyi - */ -#include <donau_config.h> -#include <gnunet/gnunet_json_lib.h> -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include <taler/taler_json_lib.h> -#include <taler/taler_mhd_lib.h> -#include <taler/taler_util.h> -#include "donau-httpd_charity.h" -#include "donau-httpd_db.h" - - -MHD_RESULT -DH_handler_charity_patch (struct DH_RequestContext *rc, - const json_t *root, - const char *const args[]) -{ - struct DONAU_CharityPublicKeyP charity_pub; - uint64_t charity_id; - char dummy; - const char *charity_name = NULL; - const char *charity_url = NULL; - struct TALER_Amount max_per_year; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("charity_pub", - &charity_pub), - GNUNET_JSON_spec_string ("charity_name", - &charity_name), - GNUNET_JSON_spec_string ("charity_url", - &charity_url), - TALER_JSON_spec_amount ("max_per_year", - DH_currency, - &max_per_year), - GNUNET_JSON_spec_end () - }; - - if ( (NULL == args[0]) || - (1 != sscanf (args[0], - "%lu%c", - &charity_id, - &dummy)) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "charity_id"); - } - - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (rc->connection, - root, - spec); - if (GNUNET_SYSERR == res) - return MHD_NO; /* hard failure */ - if (GNUNET_NO == res) - { - GNUNET_break_op (0); - return MHD_YES; /* failure */ - } - } - - { - struct DONAUDB_CharityMetaData meta; - enum GNUNET_DB_QueryStatus qs; - - qs = DH_plugin->lookup_charity (DH_plugin->cls, - charity_id, - &meta); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "lookup_charity"); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_DONAU_CHARITY_NOT_FOUND, - args[0]); - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - - if (0 < TALER_amount_cmp (&meta.receipts_to_date, - &max_per_year)) - { - GNUNET_break_op (0); - GNUNET_free (meta.charity_name); - GNUNET_free (meta.charity_url); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "max_per_year must NOT be SMALLER than receipts_to_date"); - } - - qs = DH_plugin->update_charity (DH_plugin->cls, - charity_id, - &charity_pub, - charity_name, - charity_url, - &max_per_year); - GNUNET_free (meta.charity_name); - GNUNET_free (meta.charity_url); - - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "update_charity"); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_DONAU_CHARITY_NOT_FOUND, - args[0]); - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - return TALER_MHD_reply_static (rc->connection, - MHD_HTTP_OK, - NULL, - NULL, - 0); - } - } - - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - "charity_patch"); -} - - -/* end of donau-httpd_charity_patch.c */ diff --git a/src/donau/donau-httpd_config.c b/src/donau/donau-httpd_config.c @@ -1,55 +0,0 @@ -/* - This file is part of TALER - 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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_config.c - * @brief Handle /config requests - * @author Christian Grothoff - */ -#include <donau_config.h> -#include <gnunet/gnunet_json_lib.h> -#include <taler/taler_dbevents.h> -#include "donau-httpd_config.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_mhd_lib.h> -#include <jansson.h> - - -MHD_RESULT -DH_handler_config (struct DH_RequestContext *rc, - const char *const args[]) -{ - static struct MHD_Response *resp; - - (void) args; - if (NULL == resp) - { - resp = TALER_MHD_MAKE_JSON_PACK ( - GNUNET_JSON_pack_string ("currency", - DH_currency), - GNUNET_JSON_pack_string ("name", - "donau"), - GNUNET_JSON_pack_string ("legal_domain", - DH_legal_domain), - GNUNET_JSON_pack_string ("version", - DONAU_PROTOCOL_VERSION)); - } - return MHD_queue_response (rc->connection, - MHD_HTTP_OK, - resp); -} - - -/* end of donau-httpd_config.c */ diff --git a/src/donau/donau-httpd_config.h b/src/donau/donau-httpd_config.h @@ -1,58 +0,0 @@ -/* - This file is part of TALER - (C) 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 DONAUABILITY 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 donau-httpd_config.h - * @brief headers for /config handler - * @author Christian Grothoff - */ -#ifndef DONAU_HTTPD_CONFIG_H -#define DONAU_HTTPD_CONFIG_H -#include <microhttpd.h> -#include "donau-httpd.h" - - -/** - * Taler protocol version in the format CURRENT:REVISION:AGE - * as used by GNU libtool. See - * https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html - * - * Please be very careful when updating and follow - * https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info - * precisely. Note that this version has NOTHING to do with the - * release version, and the format is NOT the same that semantic - * versioning uses either. - * - * When changing this version, you likely want to also update - * #TALER_PROTOCOL_CURRENT and #TALER_PROTOCOL_AGE in - * donau_api_handle.c! - * - * Returned via both /config and /keys endpoints. - */ -#define DONAU_PROTOCOL_VERSION "0:1:0" - - -/** - * Manages a /config call. - * - * @param rc context of the handler - * @param[in,out] args remaining arguments (ignored) - * @return MHD result code - */ -MHD_RESULT -DH_handler_config (struct DH_RequestContext *rc, - const char *const args[]); - -#endif diff --git a/src/donau/donau-httpd_csr.c b/src/donau/donau-httpd_csr.c @@ -1,126 +0,0 @@ -/* - This file is part of TALER - 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 Foundation; either version 3, - or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty - of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General - Public License along with TALER; see the file COPYING. If not, - see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_csr.c - * @brief Handle /csr requests - * @author Johannes Casaburi - */ -#include <donau_config.h> -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include <pthread.h> -#include <taler/taler_json_lib.h> -#include <taler/taler_mhd_lib.h> -#include <taler/taler_signatures.h> -#include "donaudb_plugin.h" -#include "donau-httpd_keys.h" -#include "donau-httpd_csr.h" - - -/** - * Maximum number of csr records we return per request. - */ -#define MAX_RECORDS 1024 - - -MHD_RESULT -DH_handler_csr_issue (struct DH_RequestContext *rc, - const json_t *root, - const char *const args[]) -{ - struct GNUNET_CRYPTO_CsSessionNonce nonce; - struct DONAU_DonationUnitHashP du_pub_hash; - struct GNUNET_CRYPTO_BlindingInputValues ewv = { - .cipher = GNUNET_CRYPTO_BSA_CS - }; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("nonce", - &nonce), - GNUNET_JSON_spec_fixed_auto ("du_pub_hash", - &du_pub_hash), - GNUNET_JSON_spec_end () - }; - struct DH_DonationUnitKey *dk; - - (void) args; - { - 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; - } - - { - dk = DH_keys_donation_unit_by_hash (&du_pub_hash); - if (NULL == dk) - { - GNUNET_break (0); - return TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_DONAU_GENERIC_KEYS_MISSING, - NULL); - } - if (GNUNET_CRYPTO_BSA_CS != - dk->donation_unit_pub.bsign_pub_key->cipher) - { - /* donation_unit is valid but not for CS */ - GNUNET_break (0); - return TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_DONAU_GENERIC_KEYS_MISSING, - NULL); - } - } - - /* derive r_pub */ - { - enum TALER_ErrorCode ec; - - ec = DH_keys_donation_unit_cs_r_pub (&du_pub_hash, - &nonce, - &ewv.details.cs_values); - if (TALER_EC_NONE != ec) - { - GNUNET_break (0); - return TALER_MHD_reply_with_ec (rc->connection, - ec, - NULL); - } - } - { - struct TALER_ExchangeBlindingValues exw = { - .blinding_inputs = &ewv - }; - - return TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_CREATED, - TALER_JSON_pack_exchange_blinding_values ("ewv", - &exw)); - } -} - - -/* end of donau-httpd_csr.c */ diff --git a/src/donau/donau-httpd_csr.h b/src/donau/donau-httpd_csr.h @@ -1,42 +0,0 @@ -/* - This file is part of TALER - 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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_csr.h - * @brief Handle /csr-* requests - * @author Johannes Casaburi - */ -#ifndef DONAU_HTTPD_CSR_H -#define DONAU_HTTPD_CSR_H - -#include <microhttpd.h> -#include "donau-httpd.h" -#include "donaudb_plugin.h" - - -/** - * Handle a "/csr-issue" request. - * - * @param rc request context - * @param root uploaded JSON data - * @param args empty array - * @return MHD result code - */ -MHD_RESULT -DH_handler_csr_issue (struct DH_RequestContext *rc, - const json_t *root, - const char *const args[]); - -#endif diff --git a/src/donau/donau-httpd_delete-charities-CHARITY_ID.c b/src/donau/donau-httpd_delete-charities-CHARITY_ID.c @@ -0,0 +1,85 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_delete-charities-CHARITY_ID.c + * @brief Handle DELETE /charitys/$CHARITY_ID requests. + * @author Johannes Casaburi + */ +#include "donau_config.h" +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_json_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include "taler/taler_dbevents.h" +#include "taler/taler_json_lib.h" +#include "taler/taler_mhd_lib.h" +#include "donau-httpd_delete-charities-CHARITY_ID.h" + + +MHD_RESULT +DH_handler_delete_charities ( + struct DH_RequestContext *rc, + const char *const args[1]) +{ + unsigned long long charity_id; + char dummy; + + if ( (NULL == args[0]) || + (1 != sscanf (args[0], + "%llu%c", + &charity_id, + &dummy)) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "charity_id"); + } + + { + enum GNUNET_DB_QueryStatus qs; + + qs = DH_plugin->do_charity_delete (DH_plugin->cls, + (uint64_t) charity_id); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_static ( + rc->connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); + } + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + } +} + + +/* end of donau-httpd_delete-charities-CHARITY_ID.c */ diff --git a/src/donau/donau-httpd_delete-charities-CHARITY_ID.h b/src/donau/donau-httpd_delete-charities-CHARITY_ID.h @@ -0,0 +1,40 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_delete-charities-CHARITY_ID.h + * @brief Handle DELETE /charities/$CHARITY_ID request + * @author Johannes Casaburi + */ +#ifndef DONAU_HTTPD_DELETE_CHARITIES_CHARITY_ID_H +#define DONAU_HTTPD_DELETE_CHARITIES_CHARITY_ID_H + +#include <microhttpd.h> +#include "donau-httpd.h" + + +/** + * Handle a DELETE "/charities/$CHARITY_ID" request. + * + * @param rc request details about the request to handle + * @param args argument with the charity identifier + * @return MHD result code + */ +MHD_RESULT +DH_handler_delete_charities ( + struct DH_RequestContext *rc, + const char *const args[1]); + +#endif diff --git a/src/donau/donau-httpd_donation-statement.c b/src/donau/donau-httpd_donation-statement.c @@ -1,131 +0,0 @@ -/* - This file is part of TALER - 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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_donation-statement_get.c - * @brief Return donation statement - * @author Johannes Casaburi - */ -#include <donau_config.h> -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include <pthread.h> -#include <taler/taler_json_lib.h> -#include <taler/taler_mhd_lib.h> -#include <taler/taler_signatures.h> -#include "donaudb_plugin.h" -#include "donau-httpd_keys.h" -#include "donau-httpd_donation-statement.h" - - -MHD_RESULT -DH_handler_donation_statement_get ( - struct DH_RequestContext *rc, - const char *const args[2]) -{ - unsigned long long donation_year; - struct DONAU_HashDonorTaxId h_donor_tax_id; - char dummy; - - if ( (NULL == args[0]) || - (1 != sscanf (args[0], - "%llu%c", - &donation_year, - &dummy)) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "donation_year"); - } - - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (args[1], - strlen (args[1]), - &h_donor_tax_id, - sizeof (h_donor_tax_id))) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "h_donor_tax_id"); - } - - { - struct TALER_Amount total_donations; - struct DONAU_DonauPublicKeyP donau_pub; - struct DONAU_DonauSignatureP donau_sig; - enum GNUNET_DB_QueryStatus qs; - MHD_RESULT result; - - qs = DH_plugin->iterate_submitted_receipts (DH_plugin->cls, - (uint64_t) donation_year, - &h_donor_tax_id, - &total_donations); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return TALER_MHD_reply_static ( - rc->connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - enum TALER_ErrorCode ec; - ec = DONAU_donation_statement_sign ( - &DH_keys_donau_sign_, - &total_donations, - donation_year, - &h_donor_tax_id, - &donau_pub, - &donau_sig); - - if (TALER_EC_NONE != ec) - { - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - ec, - NULL); - } - break; - } - - result = TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - TALER_JSON_pack_amount ("total", &total_donations), - GNUNET_JSON_pack_data_auto ("donation_statement_sig", - &donau_sig), - GNUNET_JSON_pack_data_auto ("donau_pub", &donau_pub)); - - return result; - } -} - - -/* end of donau-httpd_donation-statement.c */ diff --git a/src/donau/donau-httpd_donation-statement.h b/src/donau/donau-httpd_donation-statement.h @@ -1,41 +0,0 @@ -/* - This file is part of TALER - 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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_donation-statement.h - * @brief Handle /donation-statement requests - * @author Johannes Casaburi - */ -#ifndef DONAU_HTTPD_DONATION_STATEMENT_H -#define DONAU_HTTPD_DONATION_STATEMENT_H - -#include <microhttpd.h> -#include "donau-httpd.h" -#include "donaudb_plugin.h" - - -/** - * Handle a GET "/charities/$YEAR/$H_DONOR_TAX_ID" request. - * - * @param rc request context - * @param args GET arguments (should be two) - * @return MHD result code - */ -MHD_RESULT -DH_handler_donation_statement_get ( - struct DH_RequestContext *rc, - const char *const args[2]); - -#endif diff --git a/src/donau/donau-httpd_get-charities.c b/src/donau/donau-httpd_get-charities.c @@ -0,0 +1,119 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_get-charities.c + * @brief Return charities + * @author Johannes Casaburi + */ +#include <donau_config.h> +#include <gnunet/gnunet_util_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include <taler/taler_json_lib.h> +#include <taler/taler_mhd_lib.h> +#include <taler/taler_signatures.h> +#include "donau-httpd.h" +#include "donaudb_plugin.h" +#include "donau-httpd_get-charities.h" + + +/** + * Maximum number of charities we return per request. + */ +#define MAX_RECORDS 1024 + +/** + * Return charities information. + * + * @param cls closure + */ +static enum GNUNET_GenericReturnValue +charities_cb ( + void *cls, + uint64_t charity_id, + const struct DONAU_CharityPublicKeyP *charity_pub, + const char *charity_name, + const struct TALER_Amount *max_per_year, + uint32_t current_year, + const struct TALER_Amount *receipts_to_date) +{ + json_t *charities = cls; + + GNUNET_assert ( + 0 == + json_array_append_new ( + charities, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_uint64 ("charity_id", + charity_id), + GNUNET_JSON_pack_data_auto ("charity_pub", + charity_pub), + GNUNET_JSON_pack_string ("charity_name", + charity_name), + TALER_JSON_pack_amount ("max_per_year", + max_per_year), + GNUNET_JSON_pack_uint64 ("current_year", + current_year), + TALER_JSON_pack_amount ("receipts_to_date", + receipts_to_date)))); + return GNUNET_OK; +} + + +MHD_RESULT +DH_handler_get_charities ( + struct DH_RequestContext *rc) +{ + + { + json_t *charities; + enum GNUNET_DB_QueryStatus qs; + + charities = json_array (); + GNUNET_assert (NULL != charities); + qs = DH_plugin->get_charities (DH_plugin->cls, + &charities_cb, + charities); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + json_decref (charities); + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_static ( + rc->connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + return TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_array_steal ("charities", + charities)); + } +} + + +/* end of donau-httpd_get-charities.c */ diff --git a/src/donau/donau-httpd_get-charities.h b/src/donau/donau-httpd_get-charities.h @@ -0,0 +1,38 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_get-charities.h + * @brief Handle GET /charities request + * @author Johannes Casaburi + */ +#ifndef DONAU_HTTPD_GET_CHARITIES_H +#define DONAU_HTTPD_GET_CHARITIES_H + +#include <microhttpd.h> +#include "donau-httpd.h" + + +/** + * Handle a GET "/charities" request. + * + * @param rc request context + * @return MHD result code + */ +MHD_RESULT +DH_handler_get_charities ( + struct DH_RequestContext *rc); + +#endif diff --git a/src/donau/donau-httpd_get-charity-CHARITY_ID.c b/src/donau/donau-httpd_get-charity-CHARITY_ID.c @@ -0,0 +1,122 @@ +/* + This file is part of TALER + Copyright (C) 2023-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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_get-charity-CHARITY_ID.c + * @brief Return summary information about a charity + * @author Johannes Casaburi + */ +#include <donau_config.h> +#include <gnunet/gnunet_util_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include <pthread.h> +#include <taler/taler_json_lib.h> +#include <taler/taler_mhd_lib.h> +#include <taler/taler_signatures.h> +#include "donaudb_plugin.h" +#include "donau-httpd_get-charity-CHARITY_ID.h" + + +/** + * Maximum number of records we return per request. + */ +#define MAX_RECORDS 1024 + + +MHD_RESULT +DH_handler_get_charity ( + struct DH_RequestContext *rc, + const struct DONAU_CharitySignatureP *charity_sig, + const char *charity_id_s) +{ + unsigned long long charity_id; + char dummy; + + if ( (NULL == charity_id_s) || + (1 != sscanf (charity_id_s, + "%llu%c", + &charity_id, + &dummy)) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "charity_id"); + } + + { + struct DONAUDB_CharityMetaData meta; + enum GNUNET_DB_QueryStatus qs; + MHD_RESULT result; + + qs = DH_plugin->lookup_charity (DH_plugin->cls, + (uint64_t) charity_id, + &meta); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_DONAU_CHARITY_NOT_FOUND, + charity_id_s); + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + + if (GNUNET_OK != + DONAU_charity_get_info_verify (&meta.charity_pub, + charity_sig)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_GENERIC_FORBIDDEN, + DONAU_HTTP_HEADER_CHARITY_SIGNATURE); + } + result = TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_data_auto ("charity_pub", + &meta.charity_pub), + GNUNET_JSON_pack_string ("url", + meta.charity_url), + GNUNET_JSON_pack_string ("name", + meta.charity_name), + TALER_JSON_pack_amount ("max_per_year", + &meta.max_per_year), + TALER_JSON_pack_amount ("receipts_to_date", + &meta.receipts_to_date), + GNUNET_JSON_pack_uint64 ("current_year", + meta.current_year)); + + GNUNET_free (meta.charity_url); + GNUNET_free (meta.charity_name); + return result; + } +} + + +/* end of donau-httpd_get-charity-CHARITY_ID.c */ diff --git a/src/donau/donau-httpd_get-charity-CHARITY_ID.h b/src/donau/donau-httpd_get-charity-CHARITY_ID.h @@ -0,0 +1,44 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_get-charity-CHARITY_ID.h + * @brief Handle GET /charity/$CHARITY_ID request + * @author Johannes Casaburi + */ +#ifndef DONAU_HTTPD_GET_CHARITY_CHARITY_ID_H +#define DONAU_HTTPD_GET_CHARITY_CHARITY_ID_H + +#include <microhttpd.h> +#include "donau-httpd.h" +#include "donaudb_plugin.h" + + +/** + * Handle a GET "/charities/$CHARITY_ID" request. + * + * @param rc request context + * @param charity_sig signature of the charity authorizing access + * (to be checked!) + * @param charity_id_s the "$CHARITY_ID" argument + * @return MHD result code + */ +MHD_RESULT +DH_handler_get_charity ( + struct DH_RequestContext *rc, + const struct DONAU_CharitySignatureP *charity_sig, + const char *charity_id_s); + +#endif diff --git a/src/donau/donau-httpd_get-config.c b/src/donau/donau-httpd_get-config.c @@ -0,0 +1,55 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_get-config.c + * @brief Handle /config requests + * @author Christian Grothoff + */ +#include <donau_config.h> +#include <gnunet/gnunet_json_lib.h> +#include <taler/taler_dbevents.h> +#include "donau-httpd_get-config.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_mhd_lib.h> +#include <jansson.h> + + +MHD_RESULT +DH_handler_get_config (struct DH_RequestContext *rc, + const char *const args[]) +{ + static struct MHD_Response *resp; + + (void) args; + if (NULL == resp) + { + resp = TALER_MHD_MAKE_JSON_PACK ( + GNUNET_JSON_pack_string ("currency", + DH_currency), + GNUNET_JSON_pack_string ("name", + "donau"), + GNUNET_JSON_pack_string ("legal_domain", + DH_legal_domain), + GNUNET_JSON_pack_string ("version", + DONAU_PROTOCOL_VERSION)); + } + return MHD_queue_response (rc->connection, + MHD_HTTP_OK, + resp); +} + + +/* end of donau-httpd_get-config.c */ diff --git a/src/donau/donau-httpd_get-config.h b/src/donau/donau-httpd_get-config.h @@ -0,0 +1,58 @@ +/* + This file is part of TALER + (C) 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 DONAUABILITY 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 donau-httpd_get-config.h + * @brief headers for /config handler + * @author Christian Grothoff + */ +#ifndef DONAU_HTTPD_GET_CONFIG_H +#define DONAU_HTTPD_GET_CONFIG_H +#include <microhttpd.h> +#include "donau-httpd.h" + + +/** + * Taler protocol version in the format CURRENT:REVISION:AGE + * as used by GNU libtool. See + * https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html + * + * Please be very careful when updating and follow + * https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info + * precisely. Note that this version has NOTHING to do with the + * release version, and the format is NOT the same that semantic + * versioning uses either. + * + * When changing this version, you likely want to also update + * #TALER_PROTOCOL_CURRENT and #TALER_PROTOCOL_AGE in + * donau_api_handle.c! + * + * Returned via both /config and /keys endpoints. + */ +#define DONAU_PROTOCOL_VERSION "0:1:0" + + +/** + * Manages a /config call. + * + * @param rc context of the handler + * @param[in,out] args remaining arguments (ignored) + * @return MHD result code + */ +MHD_RESULT +DH_handler_get_config (struct DH_RequestContext *rc, + const char *const args[]); + +#endif diff --git a/src/donau/donau-httpd_get-donation-statement-YEAR-HASH_DONOR_ID.c b/src/donau/donau-httpd_get-donation-statement-YEAR-HASH_DONOR_ID.c @@ -0,0 +1,131 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_get-donation-statement-YEAR-HASH_DONOR_ID.c + * @brief Return donation statement + * @author Johannes Casaburi + */ +#include <donau_config.h> +#include <gnunet/gnunet_util_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include <pthread.h> +#include <taler/taler_json_lib.h> +#include <taler/taler_mhd_lib.h> +#include <taler/taler_signatures.h> +#include "donaudb_plugin.h" +#include "donau-httpd_get-keys.h" +#include "donau-httpd_get-donation-statement-YEAR-HASH_DONOR_ID.h" + + +MHD_RESULT +DH_handler_get_donation_statement ( + struct DH_RequestContext *rc, + const char *const args[2]) +{ + unsigned long long donation_year; + struct DONAU_HashDonorTaxId h_donor_tax_id; + char dummy; + + if ( (NULL == args[0]) || + (1 != sscanf (args[0], + "%llu%c", + &donation_year, + &dummy)) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "donation_year"); + } + + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[1], + strlen (args[1]), + &h_donor_tax_id, + sizeof (h_donor_tax_id))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "h_donor_tax_id"); + } + + { + struct TALER_Amount total_donations; + struct DONAU_DonauPublicKeyP donau_pub; + struct DONAU_DonauSignatureP donau_sig; + enum GNUNET_DB_QueryStatus qs; + MHD_RESULT result; + + qs = DH_plugin->iterate_submitted_receipts (DH_plugin->cls, + (uint64_t) donation_year, + &h_donor_tax_id, + &total_donations); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_static ( + rc->connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + enum TALER_ErrorCode ec; + ec = DONAU_donation_statement_sign ( + &DH_keys_donau_sign_, + &total_donations, + donation_year, + &h_donor_tax_id, + &donau_pub, + &donau_sig); + + if (TALER_EC_NONE != ec) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + ec, + NULL); + } + break; + } + + result = TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_OK, + TALER_JSON_pack_amount ("total", &total_donations), + GNUNET_JSON_pack_data_auto ("donation_statement_sig", + &donau_sig), + GNUNET_JSON_pack_data_auto ("donau_pub", &donau_pub)); + + return result; + } +} + + +/* end of donau-httpd_get-donation-statement-YEAR-HASH_DONOR_ID.c */ diff --git a/src/donau/donau-httpd_get-donation-statement-YEAR-HASH_DONOR_ID.h b/src/donau/donau-httpd_get-donation-statement-YEAR-HASH_DONOR_ID.h @@ -0,0 +1,41 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_get-donation-statement-YEAR-HASH_DONOR_ID.h + * @brief Handle /donation-statement requests + * @author Johannes Casaburi + */ +#ifndef DONAU_HTTPD_GET_DONATION_STATEMENT_YEAR_HASH_DONOR_ID_H +#define DONAU_HTTPD_GET_DONATION_STATEMENT_YEAR_HASH_DONOR_ID_H + +#include <microhttpd.h> +#include "donau-httpd.h" +#include "donaudb_plugin.h" + + +/** + * Handle a GET "/charities/$YEAR/$H_DONOR_TAX_ID" request. + * + * @param rc request context + * @param args GET arguments (should be two) + * @return MHD result code + */ +MHD_RESULT +DH_handler_get_donation_statement ( + struct DH_RequestContext *rc, + const char *const args[2]); + +#endif diff --git a/src/donau/donau-httpd_get-history.c b/src/donau/donau-httpd_get-history.c @@ -0,0 +1,121 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_get-history.c + * @brief Return history + * @author Johannes Casaburi + */ +#include "donau_config.h" +#include <gnunet/gnunet_util_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include "taler/taler_json_lib.h" +#include "taler/taler_mhd_lib.h" +#include "taler/taler_signatures.h" +#include "donau-httpd.h" +#include "donaudb_plugin.h" +#include "donau-httpd_get-history.h" + + +/** + * Maximum number of history we return per request. + */ +// #define MAX_RECORDS 1024 + +/** + * Return history information. + * + * @param cls closure + */ +static enum GNUNET_GenericReturnValue +history_cb ( + void *cls, + unsigned long long charity_id, + struct TALER_Amount final_amount, + uint64_t donation_year) +{ + json_t *history = cls; + + GNUNET_assert ( + 0 == + json_array_append ( + history, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_int64 ("charity_id", + charity_id), + TALER_JSON_pack_amount ("final_amount", + &final_amount), + GNUNET_JSON_pack_int64 ("donation_year", + donation_year)))); + + return GNUNET_OK; +} + + +MHD_RESULT +DH_handler_get_history ( + struct DH_RequestContext *rc, + const char *const args[]) +{ + + if (NULL != args[1]) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_ENDPOINT_UNKNOWN, + args[1]); + } + + { + json_t *history; + enum GNUNET_DB_QueryStatus qs; + + history = json_array (); + GNUNET_assert (NULL != history); + qs = DH_plugin->get_history (DH_plugin->cls, + &history_cb, + history); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + json_decref (history); + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_static ( + rc->connection, + MHD_HTTP_NO_CONTENT, + NULL, + NULL, + 0); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + return TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_array_steal ("history", + history)); + } +} + + +/* end of donau-httpd_get-history.c */ diff --git a/src/donau/donau-httpd_get-history.h b/src/donau/donau-httpd_get-history.h @@ -0,0 +1,51 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_get-history.h + * @brief Handle /history requests + * @author Johannes Casaburi + */ +#ifndef DONAU_HTTPD_GET_HISTORY_H +#define DONAU_HTTPD_GET_HISTORY_H + +#include <microhttpd.h> +#include "donau-httpd.h" + +/** + * Handle a GET "/history" request. + * + * @param rc request context + * @param args GET arguments (should be one) + * @return MHD result code + */ +MHD_RESULT +DH_handler_get_history ( + struct DH_RequestContext *rc, + const char *const args[]); + +/** + * Handle a GET "/history/$charity_id" request. + * + * @param rc request context + * @param args GET arguments (should be one) + * @return MHD result code + */ +MHD_RESULT +DH_handler_get_history_entry ( + struct DH_RequestContext *rc, + const char *const args[]); + +#endif diff --git a/src/donau/donau-httpd_get-keys.c b/src/donau/donau-httpd_get-keys.c @@ -0,0 +1,1454 @@ +/* + This file is part of TALER + Copyright (C) 2023-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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file donau-httpd_get-keys.c + * @brief management of our various keys + * @author Christian Grothoff + * @author Özgür Kesim + * @author Pius Loosli + * @author Johannes Casaburi + */ +#include <donau_config.h> +#include <taler/taler_json_lib.h> +#include <taler/taler_mhd_lib.h> +#include "donau_json_lib.h" +#include "donau-httpd.h" +#include "donau-httpd_get-keys.h" +#include "donau-httpd_get-config.h" +#include "donaudb_plugin.h" +#include "donau_util.h" + + +/** + * @brief All information about an donau online signing key (which is used to + * sign messages from the donau). + */ +struct SigningKey +{ + + /** + * The donau's (online signing) public key. + */ + struct DONAU_DonauPublicKeyP donau_pub; + + /** + * Meta data about the signing key, such as validity periods. + */ + struct DONAUDB_SignkeyMetaData meta; + + /** + * Did we lose the private keys? // NEEDED? + */ + bool lost; +}; + + +/** + * Snapshot of the (coin and signing) keys (including private keys) of + * the exchange. There can be multiple instances of this struct, as it is + * reference counted and only destroyed once the last user is done + * with it. The current instance is acquired using + * #TEH_KS_acquire(). Using this function increases the + * reference count. The contents of this structure (except for the + * reference counter) should be considered READ-ONLY until it is + * ultimately destroyed (as there can be many concurrent users). + */ +struct DH_KeyStateHandle +{ + + /** + * For which (global) key_generation was this data structure created? + * Used to check when we are outdated and need to be re-generated. + */ + uint64_t key_generation; + + /** + * When did we initiate the key reloading? + */ + struct GNUNET_TIME_Timestamp reload_time; + + /** + * When does our online signing key expire and we + * thus need to re-generate this response? + */ + struct GNUNET_TIME_Timestamp signature_expires; + + /** + * Response to return if the client supports (deflate) compression. + */ + struct MHD_Response *response_compressed; + + /** + * Response to return if the client does not support compression. + */ + struct MHD_Response *response_uncompressed; + + /** + * ETag for these responses. + */ + char *etag; + +}; + + +/** + * RSA security module public key, all zero if not known. + */ +static struct TALER_SecurityModulePublicKeyP donation_unit_rsa_sm_pub; + +/** + * CS security module public key, all zero if not known. + */ +static struct TALER_SecurityModulePublicKeyP donation_unit_cs_sm_pub; + +/** + * EdDSA security module public key, all zero if not known. + */ +static struct TALER_SecurityModulePublicKeyP esign_sm_pub; + +/** + * Counter incremented whenever we have a reason to re-build the keys because + * something external changed. See #DH_keys_get_state() and + * #DH_keys_update_states() for uses of this variable. + */ +static uint64_t key_generation; + +/** + * Handle for the esign/EdDSA helper. + */ +static struct TALER_CRYPTO_ExchangeSignHelper *esh; + +/** + * Handle for the donation_unit/RSA helper. + */ +static struct TALER_CRYPTO_RsaDenominationHelper *rsadh; + +/** + * Handle for the donation_unit/CS helper. + */ +static struct TALER_CRYPTO_CsDenominationHelper *csdh; + +/** + * Map from H(rsa_pub) or H(cs_pub) to `struct DH_DonationUnitKey` entries. + */ +static struct GNUNET_CONTAINER_MultiHashMap *du_keys; + +/** + * Map from `struct TALER_ExchangePublicKey` to `struct SigningKey` + * entries. Based on the fact that a `struct GNUNET_PeerIdentity` is also + * an EdDSA public key. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *esign_keys; + +/** + * Stores the latest generation of our key state. + */ +static struct DH_KeyStateHandle *key_state; + + +/** + * Add the headers we want to set for every /keys response. + * + * @param cls the key state to use + * @param[in,out] response the response to modify + */ +static void +setup_general_response_headers (void *cls, + struct MHD_Response *response) +{ + struct DH_KeyStateHandle *ksh = cls; + char dat[128]; + + TALER_MHD_add_global_headers (response, + true); + 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)); + /* 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")); +} + + +/** + * Initialize @a ksh using the given values for @a signkeys, + * and @a denoms. + * + * @param[in,out] ksh key state handle we build @a ksh for + * @param[in,out] signkeys list of sign keys to return + * @param[in,out] donation_units list of grouped denominations to return + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +create_keys_response (struct DH_KeyStateHandle *ksh, + json_t *signkeys, + json_t *donation_units) +{ + json_t *keys; + char *keys_json; + void *keys_jsonz; + size_t keys_jsonz_size; + int comp; + char etag[sizeof (struct GNUNET_HashCode) * 2]; + + GNUNET_assert (NULL != signkeys); + GNUNET_assert (NULL != donation_units); + GNUNET_assert (NULL != DH_currency); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Creating /keys response\n"); + + 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_array_incref ("signkeys", + signkeys), + GNUNET_JSON_pack_array_incref ("donation_units", + donation_units)); + GNUNET_assert (NULL != keys); + + /* 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 */ + ksh->response_uncompressed + = MHD_create_response_from_buffer (keys_jsonz_size, + keys_json, + MHD_RESPMEM_MUST_FREE); + GNUNET_assert (NULL != ksh->response_uncompressed); + setup_general_response_headers (ksh, + ksh->response_uncompressed); + GNUNET_break (MHD_YES == + MHD_add_response_header (ksh->response_uncompressed, + MHD_HTTP_HEADER_ETAG, + etag)); + /* Also compute compressed version of /keys response */ + comp = TALER_MHD_body_compress (&keys_jsonz, + &keys_jsonz_size); + ksh->response_compressed + = MHD_create_response_from_buffer (keys_jsonz_size, + keys_jsonz, + MHD_RESPMEM_MUST_FREE); + GNUNET_assert (NULL != ksh->response_compressed); + /* If the response is actually compressed, set the + respective header. */ + GNUNET_assert ( (MHD_YES != comp) || + (MHD_YES == + MHD_add_response_header (ksh->response_compressed, + MHD_HTTP_HEADER_CONTENT_ENCODING, + "deflate")) ); + setup_general_response_headers (ksh, + ksh->response_compressed); + /* Set cache control headers: our response varies depending on these headers */ + GNUNET_break (MHD_YES == + MHD_add_response_header (ksh->response_compressed, + 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 (ksh->response_compressed, + MHD_HTTP_HEADER_CACHE_CONTROL, + "public,max-age=86400")); + GNUNET_break (MHD_YES == + MHD_add_response_header (ksh->response_compressed, + MHD_HTTP_HEADER_ETAG, + etag)); + ksh->etag = GNUNET_strdup (etag); + return GNUNET_OK; +} + + +/** + * Closure for #insert_donation_unit_cb and #add_signkey_cb. + */ +struct KeysBuilderContext +{ + + /** + * Array of donation unit keys. + */ + json_t *donation_units; + + /** + * Array of signing keys. + */ + json_t *signkeys; + +}; + +/** + * Function called for all signing keys, used to build up the + * respective JSON response. + * + * @param cls a `struct KeysBuilderContext *` with the array to append keys to + * @param pid the donau public key (in type disguise) + * @param value a `struct SigningKey` + * @return #GNUNET_OK (continue to iterate) + */ +static enum GNUNET_GenericReturnValue +add_sign_key_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct KeysBuilderContext *ctx = cls; + struct SigningKey *sk = value; + + (void) pid; + GNUNET_assert ( + 0 == + json_array_append_new ( + ctx->signkeys, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_timestamp ("stamp_start", + sk->meta.valid_from), + GNUNET_JSON_pack_timestamp ("stamp_expire", + sk->meta.expire_sign), + GNUNET_JSON_pack_data_auto ("key", + &sk->donau_pub)))); + return GNUNET_OK; +} + + +/** + * Function called on all of our current and future donation unit keys + * known to the helper process. Filters out those that are current + * and adds the remaining donation unit keys (with their configuration + * data) to the JSON array. + * + * @param cls the `struct KeysBuilderContext *` + * @param h_du_pub hash of the donation unit public key + * @param value a `struct DH_DonationUnitKey` + * @return #GNUNET_OK (continue to iterate) + */ +static enum GNUNET_GenericReturnValue +insert_donation_unit_cb (void *cls, + const struct GNUNET_HashCode *h_du_pub, + void *value) +{ + struct KeysBuilderContext *kbc = cls; + struct DH_DonationUnitKey *du = value; + + (void) h_du_pub; + GNUNET_assert ( + 0 == json_array_append_new ( + kbc->donation_units, + GNUNET_JSON_PACK ( + DONAU_JSON_pack_donation_unit_pub ("donation_unit_pub", + &du->donation_unit_pub), + GNUNET_JSON_pack_uint64 ("year", + du->validity_year), + GNUNET_JSON_pack_bool ("lost", + du->lost), + TALER_JSON_pack_amount ("value", + &du->value) + ))); + 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; + struct KeysBuilderContext kbc; + + kbc.signkeys = json_array (); + GNUNET_assert (NULL != kbc.signkeys); + kbc.donation_units = json_array (); + GNUNET_assert (NULL != kbc.donation_units); + GNUNET_CONTAINER_multipeermap_iterate (esign_keys, + &add_sign_key_cb, + &kbc); + + if (0 == json_array_size (kbc.signkeys)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No online signing keys available. Refusing to generate /keys response.\n"); + ret = GNUNET_NO; + goto CLEANUP; + } + GNUNET_CONTAINER_multihashmap_iterate (du_keys, + &insert_donation_unit_cb, + &kbc); + + if (0 == json_array_size (kbc.donation_units)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No donation units available. Refusing to generate /keys response.\n"); + ret = GNUNET_NO; + goto CLEANUP; + } + + if (GNUNET_OK != + create_keys_response (ksh, + kbc.signkeys, + kbc.donation_units)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to generate key response data\n"); + goto CLEANUP; + } + + ret = GNUNET_OK; + +CLEANUP: + if (NULL != kbc.donation_units) + json_decref (kbc.donation_units); + if (NULL != kbc.signkeys) + json_decref (kbc.signkeys); + return ret; +} + + +/** + * Free donation unit key data. + * + * @param cls a `struct DH_KeyStateHandle`, unused + * @param h_donation_unit_pub hash of the donation unit public key, unused + * @param value a `struct DH_DonationUnitKey` to free + * @return #GNUNET_OK (continue to iterate) + */ +static enum GNUNET_GenericReturnValue +clear_donation_unit_cb (void *cls, + const struct GNUNET_HashCode *h_du_pub, + void *value) +{ + struct DH_DonationUnitKey *dk = value; + + (void) cls; + (void) h_du_pub; + DONAU_donation_unit_pub_free (&dk->donation_unit_pub); + GNUNET_free (dk); + return GNUNET_OK; +} + + +/** + * Free donation unit key data. + * + * @param cls a `struct DH_KeyStateHandle`, unused + * @param pid the online signing key (type-disguised), unused + * @param value a `struct SigningKey` to free + * @return #GNUNET_OK (continue to iterate) + */ +static enum GNUNET_GenericReturnValue +clear_signkey_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct SigningKey *sk = value; + + (void) cls; + (void) pid; + GNUNET_free (sk); + return GNUNET_OK; +} + + +/** + * Synchronize helper state. Polls the key helper for updates. + */ +static void +sync_key_helpers (void) +{ + TALER_CRYPTO_helper_rsa_poll (rsadh); + TALER_CRYPTO_helper_cs_poll (csdh); + TALER_CRYPTO_helper_esign_poll (esh); +} + + +/** + * Check that the given RSA security module's public key is the one + * we have pinned. If it does not match, we die hard. + * + * @param sm_pub RSA security module public key to check + */ +static void +check_donation_unit_rsa_sm_pub (const struct + TALER_SecurityModulePublicKeyP *sm_pub) +{ + if (0 != + GNUNET_memcmp (sm_pub, + &donation_unit_rsa_sm_pub)) + { + if (! GNUNET_is_zero (&donation_unit_rsa_sm_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Our RSA security module changed its key. This must not happen.\n") + ; + GNUNET_assert (0); + } + donation_unit_rsa_sm_pub = *sm_pub; /* TOFU ;-) */ + } +} + + +/** + * Check that the given CS security module's public key is the one + * we have pinned. If it does not match, we die hard. + * + * @param sm_pub RSA security module public key to check + */ +static void +check_donation_unit_cs_sm_pub (const struct + TALER_SecurityModulePublicKeyP *sm_pub) +{ + if (0 != + GNUNET_memcmp (sm_pub, + &donation_unit_cs_sm_pub)) + { + if (! GNUNET_is_zero (&donation_unit_cs_sm_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Our CS security module changed its key. This must not happen.\n") + ; + GNUNET_assert (0); + } + donation_unit_cs_sm_pub = *sm_pub; /* TOFU ;-) */ + } +} + + +/** + * Check that the given EdDSA security module's public key is the one + * we have pinned. If it does not match, we die hard. + * + * @param sm_pub EdDSA security module public key to check + */ +static void +check_esign_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub) +{ + if (0 != + GNUNET_memcmp (sm_pub, + &esign_sm_pub)) + { + if (! GNUNET_is_zero (&esign_sm_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Our EdDSA security module changed its key. This must not happen.\n") + ; + GNUNET_assert (0); + } + esign_sm_pub = *sm_pub; /* TOFU ;-) */ + } +} + + +void +DH_keys_finished () +{ + if (NULL != rsadh) + { + TALER_CRYPTO_helper_rsa_disconnect (rsadh); + rsadh = NULL; + } + if (NULL != csdh) + { + TALER_CRYPTO_helper_cs_disconnect (csdh); + csdh = NULL; + } + if (NULL != esh) + { + TALER_CRYPTO_helper_esign_disconnect (esh); + esh = NULL; + } + if (NULL != du_keys) + { + GNUNET_CONTAINER_multihashmap_iterate (du_keys, + &clear_donation_unit_cb, + NULL); + GNUNET_CONTAINER_multihashmap_destroy (du_keys); + du_keys = NULL; + } + if (NULL != esign_keys) + { + GNUNET_CONTAINER_multipeermap_iterate (esign_keys, + &clear_signkey_cb, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (esign_keys); + esign_keys = NULL; + } +} + + +static void +destroy_key_state (struct DH_KeyStateHandle *ksh) +{ + if (NULL != ksh->response_compressed) + MHD_destroy_response (ksh->response_compressed); + if (NULL != ksh->response_uncompressed) + MHD_destroy_response (ksh->response_uncompressed); + GNUNET_free (ksh->etag); + GNUNET_free (ksh); +} + + +/** + * 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 NULL + * @param section_name name of the donation_unit 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 donation_unit_pub that is available (or was purged) + * @param bs_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 + */ +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, + struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub, + const struct TALER_SecurityModulePublicKeyP *sm_pub, + const struct TALER_SecurityModuleSignatureP *sm_sig) +{ + struct DH_DonationUnitKey *du; + struct TALER_Amount value; + enum GNUNET_DB_QueryStatus qs; + + (void) cls; + (void) sm_sig; /* not using offline signatures */ + GNUNET_assert (GNUNET_CRYPTO_BSA_RSA == bs_pub->cipher); + if (GNUNET_OK != + TALER_config_get_amount (DH_cfg, + section_name, + "value", + &value)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "RSA helper provided key for configuration section `%s' that has no `value' option set\n", + section_name); + return; + } + /* FIXME: could additionally sanity-check that this + section actually has CIPHER = RSA, etc. */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "RSA helper announces key %s for donation_unit type %s with validity %s\n", + GNUNET_h2s (&h_rsa->hash), + section_name, + GNUNET_STRINGS_relative_time_to_string (validity_duration, + false)); + du = GNUNET_CONTAINER_multihashmap_get (du_keys, + &h_rsa->hash); + if (NULL != du) + { + /* only update 'lost' status */ + du->lost = GNUNET_TIME_relative_is_zero (validity_duration); + return; + } + GNUNET_assert (NULL != sm_pub); + check_donation_unit_rsa_sm_pub (sm_pub); + + du = GNUNET_new (struct DH_DonationUnitKey); + du->h_donation_unit_pub.hash = h_rsa->hash; + du->donation_unit_pub.bsign_pub_key + = GNUNET_CRYPTO_bsign_pub_incref (bs_pub); + du->validity_year = GNUNET_TIME_time_to_year (start_time.abs_time); + du->value = value; + du->lost = GNUNET_TIME_relative_is_zero (validity_duration); + + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put ( + du_keys, + &du->h_donation_unit_pub.hash, + du, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + + qs = DH_plugin->insert_donation_unit ( + DH_plugin->cls, + &du->h_donation_unit_pub, + &du->donation_unit_pub, + du->validity_year, + &du->value); + if (qs < 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to insert donation units\n"); + GNUNET_SCHEDULER_shutdown (); + DH_global_ret = EXIT_FAILURE; + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Inserted RSA donation unit of %s\n", + TALER_amount2s (&value)); + key_generation++; +} + + +/** + * 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 NULL + * @param section_name name of the donation unit 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 donation_unit_pub that is available (or was purged) + * @param sm_pub the public key itself, NULL if the key was revoked or purged + * @param sm_pub signature by the security module + */ +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, + struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub, + const struct TALER_SecurityModulePublicKeyP *sm_pub, + const struct TALER_SecurityModuleSignatureP *sm_sig) +{ + struct DH_DonationUnitKey *du; + struct TALER_Amount value; + enum GNUNET_DB_QueryStatus qs; + + (void) cls; + (void) sm_sig; /* we are not using offline signatures */ + GNUNET_assert (GNUNET_CRYPTO_BSA_CS == bs_pub->cipher); + if (GNUNET_OK != + TALER_config_get_amount (DH_cfg, + section_name, + "value", + &value)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "CS helper provided key for configuration section `%s' that has no `value' option set\n", + section_name); + return; + } + /* FIXME: could additionally sanity-check that this + section actually has CIPHER = CS, etc. */ + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "CS helper announces key %s for donation unit type %s with validity %s\n", + GNUNET_h2s (&h_cs->hash), + section_name, + GNUNET_STRINGS_relative_time_to_string (validity_duration, + false)); + du = GNUNET_CONTAINER_multihashmap_get (du_keys, + &h_cs->hash); + if (NULL != du) + { + /* should be just an update (revocation!), so update existing entry */ + du->lost = GNUNET_TIME_relative_is_zero (validity_duration); + return; + } + GNUNET_assert (NULL != sm_pub); + check_donation_unit_cs_sm_pub (sm_pub); + + du = GNUNET_new (struct DH_DonationUnitKey); + du->h_donation_unit_pub.hash = h_cs->hash; + du->donation_unit_pub.bsign_pub_key + = GNUNET_CRYPTO_bsign_pub_incref (bs_pub); + du->validity_year = GNUNET_TIME_time_to_year (start_time.abs_time); + du->value = value; + du->lost = GNUNET_TIME_relative_is_zero (validity_duration); + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put ( + du_keys, + &du->h_donation_unit_pub.hash, + du, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + + qs = DH_plugin->insert_donation_unit ( + DH_plugin->cls, + &du->h_donation_unit_pub, + &du->donation_unit_pub, + du->validity_year, + &du->value); + if (qs < 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to insert donation units\n"); + GNUNET_SCHEDULER_shutdown (); + DH_global_ret = EXIT_FAILURE; + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Inserted CS donation unit of %s\n", + TALER_amount2s (&value)); + + key_generation++; +} + + +/** + * 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 NULL + * @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 donau_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 + */ +static void +helper_esign_cb ( + void *cls, + struct GNUNET_TIME_Timestamp start_time, + struct GNUNET_TIME_Relative validity_duration, + const struct TALER_ExchangePublicKeyP *donau_pub, + const struct TALER_SecurityModulePublicKeyP *sm_pub, + const struct TALER_SecurityModuleSignatureP *sm_sig) +{ + struct SigningKey *sk; + struct GNUNET_PeerIdentity pid; + unsigned long long expire_legal; + /* need to "cast" because secmod works with TALER_ExchangePublicKeyP */ + struct DONAU_DonauPublicKeyP donau_pubkey = { + .eddsa_pub = donau_pub->eddsa_pub + }; + enum GNUNET_DB_QueryStatus qs; + + (void) cls; + (void) sm_sig; /* not using offline signing */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "EdDSA helper announces signing key %s with validity %s\n", + TALER_B2S (donau_pub), + GNUNET_STRINGS_relative_time_to_string (validity_duration, + false)); + + pid.public_key = donau_pub->eddsa_pub; + sk = GNUNET_CONTAINER_multipeermap_get (esign_keys, + &pid); + if (NULL != sk) + { + /* should be just an update (revocation!), so update existing entry */ + sk->lost = GNUNET_TIME_relative_is_zero (validity_duration); + return; + } + GNUNET_assert (NULL != sm_pub); + check_esign_sm_pub (sm_pub); + + sk = GNUNET_new (struct SigningKey); + sk->donau_pub = donau_pubkey; + sk->meta.valid_from = start_time; + sk->meta.expire_sign + = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_absolute_add (start_time.abs_time, + validity_duration)); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (DH_cfg, + "donau", + "EXPIRE_LEGAL_YEARS", + &expire_legal)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Need EXPIRE_LEGAL_YEARS in section `donau'\n"); + GNUNET_SCHEDULER_shutdown (); + DH_global_ret = EXIT_FAILURE; + return; + } + sk->meta.expire_legal + = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_absolute_add (start_time.abs_time, + GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_YEARS, + expire_legal))); + + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multipeermap_put ( + esign_keys, + &pid, + sk, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + + qs = DH_plugin->insert_signing_key ( + DH_plugin->cls, + &donau_pubkey, + &sk->meta); + if (qs < 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to insert donation units\n"); + GNUNET_SCHEDULER_shutdown (); + DH_global_ret = EXIT_FAILURE; + return; + } + + key_generation++; +} + + +enum GNUNET_GenericReturnValue +DH_keys_init () +{ + du_keys + = GNUNET_CONTAINER_multihashmap_create (1024, + GNUNET_YES); + esign_keys + = GNUNET_CONTAINER_multipeermap_create (32, + GNUNET_NO /* MUST BE NO! */); + rsadh = TALER_CRYPTO_helper_rsa_connect (DH_cfg, + "donau", + &helper_rsa_cb, + NULL); + if (NULL == rsadh) + { + GNUNET_break (0); + DH_keys_finished (); + return GNUNET_SYSERR; + } + csdh = TALER_CRYPTO_helper_cs_connect (DH_cfg, + "donau", + &helper_cs_cb, + NULL); + if (NULL == csdh) + { + GNUNET_break (0); + DH_keys_finished (); + return GNUNET_SYSERR; + } + esh = TALER_CRYPTO_helper_esign_connect (DH_cfg, + "donau", + &helper_esign_cb, + NULL); + if (NULL == esh) + { + GNUNET_break (0); + DH_keys_finished (); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Function called with information about the donau's donation_unit keys. + * + * @param cls NULL + * @param donation_unit_pub public key of the donation_unit + * @param h_donation_unit_pub hash of @a donation_unit_pub + * @param validity_year of the donation unit + * @param value of the donation unit + */ +static enum GNUNET_GenericReturnValue +donation_unit_info_cb ( + void *cls, + const struct DONAU_DonationUnitHashP *h_donation_unit_pub, + const struct DONAU_DonationUnitPublicKey *donation_unit_pub, + uint64_t validity_year, + struct TALER_Amount *value) +{ + struct DH_DonationUnitKey *du; + + (void) cls; + GNUNET_assert (GNUNET_CRYPTO_BSA_INVALID != + donation_unit_pub->bsign_pub_key->cipher); + du = GNUNET_CONTAINER_multihashmap_get (du_keys, + &h_donation_unit_pub->hash); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Got %s key from database\n", + NULL == du ? "unknown" : "known"); + if (NULL != du) + { + /* we already know this, nothing to do */ + return GNUNET_OK; + } + + du = GNUNET_new (struct DH_DonationUnitKey); + du->h_donation_unit_pub = *h_donation_unit_pub; + DONAU_donation_unit_pub_deep_copy (&du->donation_unit_pub, + donation_unit_pub); + du->validity_year = validity_year; + du->value = *value; + du->lost = true; /* no private key known, that can only come from the helper! */ + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (du_keys, + &du->h_donation_unit_pub.hash, + du, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) + ); + return GNUNET_OK; +} + + +/** + * Function called with information about the donau's online signing keys. + * + * @param cls NULL + * @param donau_pub the public key + * @param meta meta data information about the denomination type (expirations) + */ +static void +iterate_active_signing_keys_cb ( + void *cls, + const struct DONAU_DonauPublicKeyP *donau_pub, + struct DONAUDB_SignkeyMetaData *meta) +{ + /* The 'pid' is used as the key in the "peer" map... */ + struct GNUNET_PeerIdentity pid = { + .public_key = donau_pub->eddsa_pub + }; + struct SigningKey *sk; + + (void) cls; + sk = GNUNET_CONTAINER_multipeermap_get (esign_keys, + &pid); + if (NULL != sk) + { + /* should be just an update (revocation!), so update existing entry */ + return; + } + sk = GNUNET_new (struct SigningKey); + sk->donau_pub = *donau_pub; + sk->meta = *meta; + sk->lost = true; /* no private key known, that can only come from the helper! */ + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multipeermap_put (esign_keys, + &pid, + sk, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) + ); +} + + +/** + * Create a key state. + * + * @return NULL on error (i.e. failed to access database) + */ +static struct DH_KeyStateHandle * +build_key_state () +{ + 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; + + /* NOTE: fetches master-signed signkeys, but ALSO those that were revoked! */ + GNUNET_break (GNUNET_OK == + DH_plugin->preflight (DH_plugin->cls)); + qs = DH_plugin->iterate_donation_units (DH_plugin->cls, + &donation_unit_info_cb, + NULL); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Fetched %d donation unit keys from DB\n", + (int) qs); + if (qs < 0) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); + GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); + destroy_key_state (ksh); + return NULL; + } + + /* NOTE: ONLY fetches active signkeys! */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Fetching active signing keys from DB\n"); + qs = DH_plugin->iterate_active_signing_keys (DH_plugin->cls, + &iterate_active_signing_keys_cb, + NULL); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Fetched %d active signing keys from DB\n", + (int) qs); + if (qs < 0) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); + GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); + destroy_key_state (ksh); + return NULL; + } + + 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); + return NULL; + } + + return ksh; +} + + +static struct DH_KeyStateHandle* +DH_keys_get_state () +{ + struct DH_KeyStateHandle *old_ksh; + struct DH_KeyStateHandle *ksh; + + old_ksh = key_state; + if (NULL == old_ksh) + { + ksh = build_key_state (); + 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 (); + key_state = ksh; + destroy_key_state (old_ksh); + return ksh; + } + return old_ksh; +} + + +MHD_RESULT +DH_handler_get_keys (struct DH_RequestContext *rc, + const char *const args[]) +{ + struct MHD_Connection *connection = rc->connection; + struct DH_KeyStateHandle *ksh; + struct MHD_Response *resp; + + (void) args; + sync_key_helpers (); + ksh = DH_keys_get_state (); + if (NULL == ksh) + { + if ( (GNUNET_is_zero (&donation_unit_rsa_sm_pub)) && + (GNUNET_is_zero (&donation_unit_cs_sm_pub)) ) + { + /* Either IPC failed, or neither helper had any donation_unit configured. */ + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_GATEWAY, + TALER_EC_DONAU_DONATION_UNIT_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_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to build /keys response?\n"); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_SERVICE_UNAVAILABLE, + TALER_EC_DONAU_GENERIC_KEYS_MISSING, + "failed to create keys response"); + } + resp = (TALER_MHD_CT_DEFLATE == + TALER_MHD_can_compress (connection, + TALER_MHD_CT_DEFLATE)) + ? ksh->response_compressed + : ksh->response_uncompressed; + GNUNET_assert (NULL != resp); + return MHD_queue_response (connection, + MHD_HTTP_OK, + resp); + +} + + +enum TALER_ErrorCode +DH_keys_donau_sign_ ( + const struct GNUNET_CRYPTO_SignaturePurpose *purpose, + struct DONAU_DonauPublicKeyP *pub, + struct DONAU_DonauSignatureP *sig) +{ + struct DH_KeyStateHandle *ksh; + enum TALER_ErrorCode ec; + + ksh = DH_keys_get_state (); + if (NULL == ksh) + { + /* This *can* happen if the Donau's crypto helper is not running + or had some bad error. */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Cannot sign request, no valid signing keys available.\n"); + return TALER_EC_DONAU_GENERIC_KEYS_MISSING; + } + + { + /* need to "cast" because TALER_CRYPTO works with TALER_Exchange.. */ + struct TALER_ExchangePublicKeyP donau_pub; + struct TALER_ExchangeSignatureP donau_sig; + + ec = TALER_CRYPTO_helper_esign_sign_ (esh, + purpose, + &donau_pub, + &donau_sig); + if (TALER_EC_NONE != ec) + return ec; + pub->eddsa_pub = donau_pub.eddsa_pub; + sig->eddsa_sig = donau_sig.eddsa_signature; + } + return ec; +} + + +enum TALER_ErrorCode +DH_keys_donation_unit_batch_sign ( + unsigned int num_bkps, + const struct DONAU_BkpSignData bkps[num_bkps], + struct DONAU_BlindedDonationUnitSignature du_sigs[num_bkps]) +{ + struct DH_KeyStateHandle *ksh; + struct DH_DonationUnitKey *du; + struct TALER_CRYPTO_RsaSignRequest rsrs[num_bkps]; + struct TALER_CRYPTO_CsSignRequest csrs[num_bkps]; + struct TALER_BlindedDenominationSignature rs[num_bkps]; + struct TALER_BlindedDenominationSignature cs[num_bkps]; + unsigned int rsrs_pos = 0; + unsigned int csrs_pos = 0; + enum TALER_ErrorCode ec; + + ksh = DH_keys_get_state (); + if (NULL == ksh) + return TALER_EC_DONAU_GENERIC_KEYS_MISSING; + for (unsigned int i = 0; i<num_bkps; i++) + { + const struct DONAU_DonationUnitHashP *h_du_pub = + bkps[i].h_donation_unit_pub; + const struct DONAU_BlindedUniqueDonorIdentifier *budi = bkps[i].budi; + + du = GNUNET_CONTAINER_multihashmap_get (du_keys, + &h_du_pub->hash); + if (NULL == du) + return TALER_EC_DONAU_GENERIC_DONATION_UNIT_UNKNOWN; + if (budi->blinded_message->cipher != + du->donation_unit_pub.bsign_pub_key->cipher) + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + switch (du->donation_unit_pub.bsign_pub_key->cipher) + { + case GNUNET_CRYPTO_BSA_RSA: + /* See DONAU_donation_unit_pub_hash: we guarantee that these + hashes are equivalent! */ + rsrs[rsrs_pos].h_rsa + = (const struct TALER_RsaPubHashP *) &du->h_donation_unit_pub; + rsrs[rsrs_pos].msg + = budi->blinded_message->details.rsa_blinded_message.blinded_msg; + rsrs[rsrs_pos].msg_size + = budi->blinded_message->details.rsa_blinded_message.blinded_msg_size; + rsrs_pos++; + break; + case GNUNET_CRYPTO_BSA_CS: + /* See DONAU_donation_unit_pub_hash: we guarantee that these + hashes are equivalent! */ + csrs[csrs_pos].h_cs + = (const struct TALER_CsPubHashP *) &du->h_donation_unit_pub; + csrs[csrs_pos].blinded_planchet + = &budi->blinded_message->details.cs_blinded_message; + csrs_pos++; + break; + default: + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + } + } + + if ( (0 != csrs_pos) && + (0 != rsrs_pos) ) + { + memset (rs, + 0, + sizeof (rs)); + memset (cs, + 0, + sizeof (cs)); + } + ec = TALER_EC_NONE; + if (0 != csrs_pos) + { + ec = TALER_CRYPTO_helper_cs_batch_sign ( + csdh, + csrs_pos, + csrs, + false, // for_melt + cs); + if (TALER_EC_NONE != ec) + { + for (unsigned int i = 0; i<csrs_pos; i++) + { + if (NULL != cs[i].blinded_sig) + { + GNUNET_CRYPTO_blinded_sig_decref (cs[i].blinded_sig); + cs[i].blinded_sig = NULL; + } + } + return ec; + } + } + if (0 != rsrs_pos) + { + ec = TALER_CRYPTO_helper_rsa_batch_sign ( + rsadh, + rsrs_pos, + rsrs, + rs); + if (TALER_EC_NONE != ec) + { + for (unsigned int i = 0; i<csrs_pos; i++) + { + if (NULL != cs[i].blinded_sig) + { + GNUNET_CRYPTO_blinded_sig_decref (cs[i].blinded_sig); + cs[i].blinded_sig = NULL; + } + } + for (unsigned int i = 0; i<rsrs_pos; i++) + { + if (NULL != rs[i].blinded_sig) + { + GNUNET_CRYPTO_blinded_sig_decref (rs[i].blinded_sig); + rs[i].blinded_sig = NULL; + } + } + return ec; + } + } + + rsrs_pos = 0; + csrs_pos = 0; + for (unsigned int i = 0; i<num_bkps; i++) + { + const struct DONAU_BlindedUniqueDonorIdentifier *budi = bkps[i].budi; + + switch (budi->blinded_message->cipher) + { + case GNUNET_CRYPTO_BSA_RSA: + du_sigs[i].blinded_sig = rs[rsrs_pos++].blinded_sig; + break; + case GNUNET_CRYPTO_BSA_CS: + du_sigs[i].blinded_sig = cs[csrs_pos++].blinded_sig; + break; + default: + GNUNET_assert (0); + } + } + return TALER_EC_NONE; +} + + +struct DH_DonationUnitKey * +DH_keys_donation_unit_by_hash ( + const struct DONAU_DonationUnitHashP *h_du_pub) +{ + return GNUNET_CONTAINER_multihashmap_get (du_keys, + &h_du_pub->hash); +} + + +enum TALER_ErrorCode +DH_keys_donation_unit_cs_r_pub ( + const struct DONAU_DonationUnitHashP *h_donation_unit_pub, + const struct GNUNET_CRYPTO_CsSessionNonce *nonce, + struct GNUNET_CRYPTO_CSPublicRPairP *r_pub) +{ + struct DH_DonationUnitKey *dk; + + dk = DH_keys_donation_unit_by_hash (h_donation_unit_pub); + if (NULL == dk) + { + return TALER_EC_DONAU_GENERIC_DONATION_UNIT_UNKNOWN; + } + if (GNUNET_CRYPTO_BSA_CS != + dk->donation_unit_pub.bsign_pub_key->cipher) + { + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + } + + { + struct TALER_CRYPTO_CsDeriveRequest cdr = { + .h_cs = (const struct TALER_CsPubHashP *) &dk->h_donation_unit_pub, + .nonce = nonce + }; + return TALER_CRYPTO_helper_cs_r_derive (csdh, + &cdr, + false, + r_pub); + } +} + + +/* end of donau-httpd_get-keys.c */ diff --git a/src/donau/donau-httpd_get-keys.h b/src/donau/donau-httpd_get-keys.h @@ -0,0 +1,207 @@ +/* + This file is part of TALER + Copyright (C) 2023-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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_get-keys.h + * @brief management of our various keys + * @author Christian Grothoff + */ +#include <donau_config.h> +#include <taler/taler_json_lib.h> +#include <taler/taler_mhd_lib.h> +#include "donau_util.h" +#include "donaudb_plugin.h" +#include "donau-httpd.h" + +#ifndef DONAU_HTTPD_GET_KEYS_H +#define DONAU_HTTPD_GET_KEYS_H + + +/** + * @brief All information about a donation unit key (which is used to + * sign donation receipts into existence). + */ +struct DH_DonationUnitKey +{ + + /** + * Hash code of the donation unit public key. + */ + struct DONAU_DonationUnitHashP h_donation_unit_pub; + + /** + * Decoded donation unit public key (the hash of it is in + * @e issue, but we sometimes need the full public key as well). + */ + struct DONAU_DonationUnitPublicKey donation_unit_pub; + + /** + * The validity year. + */ + uint64_t validity_year; + + /** + * Value that the donation unit represents. + */ + struct TALER_Amount value; + + /** + * Did we lose the private keys? + */ + bool lost; + +}; + +/** + * Information needed to create a blind signature. + */ +struct DH_BlindSignData +{ + /** + * Hash of key to sign with. + */ + const struct DONAU_DonationUnitHashP *h_du_pub; + + /** + * Blinded planchet to sign over. + */ + const struct DONAU_BlindedUniqueDonorIdentifier *budi; +}; + +/** + * Sign the message in @a purpose with the doanu's signing key. + * + * The @a purpose data is the beginning of the data of which the signature is + * to be created. The `size` field in @a purpose must correctly indicate the + * number of bytes of the data structure, including its header. Use + * #DH_keys_doanu_sign() instead of calling this function directly! + * + * @param purpose the message to sign + * @param[out] pub set to the current public signing key of the doanu + * @param[out] sig signature over purpose using current signing key + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +DH_keys_donau_sign_ ( + const struct GNUNET_CRYPTO_SignaturePurpose *purpose, + struct DONAU_DonauPublicKeyP *pub, + struct DONAU_DonauSignatureP *sig); + +/** + * @ingroup crypto + * @brief EdDSA sign a given block. + * + * The @a ps data must be a fixed-size struct for which the signature is to be + * created. The `size` field in @a ps->purpose must correctly indicate the + * number of bytes of the data structure, including its header. + * + * @param ps packed struct with what to sign, MUST begin with a purpose + * @param[out] pub where to store the public key to use for the signing + * @param[out] sig where to write the signature + * @return #TALER_EC_NONE on success + */ +#define DH_keys_donau_sign(ps,pub,sig) \ + ({ \ + /* check size is set correctly */ \ + GNUNET_assert (htonl ((ps)->purpose.size) == \ + sizeof (*ps)); \ + /* check 'ps' begins with the purpose */ \ + GNUNET_static_assert (((void*) (ps)) == \ + ((void*) &(ps)->purpose)); \ + DH_keys_donau_sign_ (&(ps)->purpose, \ + pub, \ + sig); \ + }) + +/** + * Resumes all suspended /keys requests, we may now have key material + * (or are shutting down). + * + * @param do_shutdown are we shutting down? + */ +void +TEH_resume_keys_requests (bool do_shutdown); + + +/** + * Function to call to handle requests to "/keys" by sending + * back our current key material. + * + * @param rc request context + * @param args array of additional options (must be empty for this function) + * @return MHD result code + */ +MHD_RESULT +DH_handler_get_keys (struct DH_RequestContext *rc, + const char *const args[]); + + +/** + * Look up the issue for a donation unit public key. + * + * @param h_du_pub hash of donation unit public key + * @return the donation unit key issue, + * or NULL if @a h_du_pub could not be found + */ +struct DH_DonationUnitKey * +DH_keys_donation_unit_by_hash ( + const struct DONAU_DonationUnitHashP *h_du_pub); + + +/** + * Initialize keys subsystem. + * + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +DH_keys_init (void); + + +/** + * Fully clean up keys subsystem. + */ +void +DH_keys_finished (void); + + +/** + * Request to sign @a budis. + * + * @param num_bkps length of @a budis array + * @param bkps array with data to blindly sign (and keys to sign with) + * @param[out] du_sigs array set to the blind signature on success; must be of length @a num_bkps + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +DH_keys_donation_unit_batch_sign ( + unsigned int num_bkps, + const struct DONAU_BkpSignData bkps[num_bkps], + struct DONAU_BlindedDonationUnitSignature du_sigs[num_bkps]); + +/** + * Request to derive CS @a r_pub using the donation_unit and nonce from @a cdd. + * + * @param h_donation_unit_pub hash to compute @a r_pub from + * @param nonce + * @param[out] r_pub where to write the result + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +DH_keys_donation_unit_cs_r_pub ( + const struct DONAU_DonationUnitHashP *h_donation_unit_pub, + const struct GNUNET_CRYPTO_CsSessionNonce *nonce, + struct GNUNET_CRYPTO_CSPublicRPairP *r_pub); + +#endif diff --git a/src/donau/donau-httpd_history.h b/src/donau/donau-httpd_history.h @@ -1,51 +0,0 @@ -/* - This file is part of TALER - 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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_history.h - * @brief Handle /history requests - * @author Johannes Casaburi - */ -#ifndef DONAU_HTTPD_HISTORY_H -#define DONAU_HTTPD_HISTORY_H - -#include <microhttpd.h> -#include "donau-httpd.h" - -/** - * Handle a GET "/history" request. - * - * @param rc request context - * @param args GET arguments (should be one) - * @return MHD result code - */ -MHD_RESULT -DH_handler_history_get ( - struct DH_RequestContext *rc, - const char *const args[]); - -/** - * Handle a GET "/history/$charity_id" request. - * - * @param rc request context - * @param args GET arguments (should be one) - * @return MHD result code - */ -MHD_RESULT -DH_handler_history_entry_get ( - struct DH_RequestContext *rc, - const char *const args[]); - -#endif diff --git a/src/donau/donau-httpd_history_get.c b/src/donau/donau-httpd_history_get.c @@ -1,121 +0,0 @@ -/* - This file is part of TALER - 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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_get-history.c - * @brief Return history - * @author Johannes Casaburi - */ -#include "donau_config.h" -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include "taler/taler_json_lib.h" -#include "taler/taler_mhd_lib.h" -#include "taler/taler_signatures.h" -#include "donau-httpd.h" -#include "donaudb_plugin.h" -#include "donau-httpd_history.h" - - -/** - * Maximum number of history we return per request. - */ -// #define MAX_RECORDS 1024 - -/** - * Return history information. - * - * @param cls closure - */ -static enum GNUNET_GenericReturnValue -history_cb ( - void *cls, - unsigned long long charity_id, - struct TALER_Amount final_amount, - uint64_t donation_year) -{ - json_t *history = cls; - - GNUNET_assert ( - 0 == - json_array_append ( - history, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_int64 ("charity_id", - charity_id), - TALER_JSON_pack_amount ("final_amount", - &final_amount), - GNUNET_JSON_pack_int64 ("donation_year", - donation_year)))); - - return GNUNET_OK; -} - - -MHD_RESULT -DH_handler_history_get ( - struct DH_RequestContext *rc, - const char *const args[]) -{ - - if (NULL != args[1]) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_ENDPOINT_UNKNOWN, - args[1]); - } - - { - json_t *history; - enum GNUNET_DB_QueryStatus qs; - - history = json_array (); - GNUNET_assert (NULL != history); - qs = DH_plugin->get_history (DH_plugin->cls, - &history_cb, - history); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - case GNUNET_DB_STATUS_SOFT_ERROR: - json_decref (history); - GNUNET_break (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return TALER_MHD_reply_static ( - rc->connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - return TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - GNUNET_JSON_pack_array_steal ("history", - history)); - } -} - - -/* end of donau-httpd_get-history.c */ diff --git a/src/donau/donau-httpd_keys.c b/src/donau/donau-httpd_keys.c @@ -1,1454 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023-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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ -/** - * @file donau-httpd_keys.c - * @brief management of our various keys - * @author Christian Grothoff - * @author Özgür Kesim - * @author Pius Loosli - * @author Johannes Casaburi - */ -#include <donau_config.h> -#include <taler/taler_json_lib.h> -#include <taler/taler_mhd_lib.h> -#include "donau_json_lib.h" -#include "donau-httpd.h" -#include "donau-httpd_keys.h" -#include "donau-httpd_config.h" -#include "donaudb_plugin.h" -#include "donau_util.h" - - -/** - * @brief All information about an donau online signing key (which is used to - * sign messages from the donau). - */ -struct SigningKey -{ - - /** - * The donau's (online signing) public key. - */ - struct DONAU_DonauPublicKeyP donau_pub; - - /** - * Meta data about the signing key, such as validity periods. - */ - struct DONAUDB_SignkeyMetaData meta; - - /** - * Did we lose the private keys? // NEEDED? - */ - bool lost; -}; - - -/** - * Snapshot of the (coin and signing) keys (including private keys) of - * the exchange. There can be multiple instances of this struct, as it is - * reference counted and only destroyed once the last user is done - * with it. The current instance is acquired using - * #TEH_KS_acquire(). Using this function increases the - * reference count. The contents of this structure (except for the - * reference counter) should be considered READ-ONLY until it is - * ultimately destroyed (as there can be many concurrent users). - */ -struct DH_KeyStateHandle -{ - - /** - * For which (global) key_generation was this data structure created? - * Used to check when we are outdated and need to be re-generated. - */ - uint64_t key_generation; - - /** - * When did we initiate the key reloading? - */ - struct GNUNET_TIME_Timestamp reload_time; - - /** - * When does our online signing key expire and we - * thus need to re-generate this response? - */ - struct GNUNET_TIME_Timestamp signature_expires; - - /** - * Response to return if the client supports (deflate) compression. - */ - struct MHD_Response *response_compressed; - - /** - * Response to return if the client does not support compression. - */ - struct MHD_Response *response_uncompressed; - - /** - * ETag for these responses. - */ - char *etag; - -}; - - -/** - * RSA security module public key, all zero if not known. - */ -static struct TALER_SecurityModulePublicKeyP donation_unit_rsa_sm_pub; - -/** - * CS security module public key, all zero if not known. - */ -static struct TALER_SecurityModulePublicKeyP donation_unit_cs_sm_pub; - -/** - * EdDSA security module public key, all zero if not known. - */ -static struct TALER_SecurityModulePublicKeyP esign_sm_pub; - -/** - * Counter incremented whenever we have a reason to re-build the keys because - * something external changed. See #DH_keys_get_state() and - * #DH_keys_update_states() for uses of this variable. - */ -static uint64_t key_generation; - -/** - * Handle for the esign/EdDSA helper. - */ -static struct TALER_CRYPTO_ExchangeSignHelper *esh; - -/** - * Handle for the donation_unit/RSA helper. - */ -static struct TALER_CRYPTO_RsaDenominationHelper *rsadh; - -/** - * Handle for the donation_unit/CS helper. - */ -static struct TALER_CRYPTO_CsDenominationHelper *csdh; - -/** - * Map from H(rsa_pub) or H(cs_pub) to `struct DH_DonationUnitKey` entries. - */ -static struct GNUNET_CONTAINER_MultiHashMap *du_keys; - -/** - * Map from `struct TALER_ExchangePublicKey` to `struct SigningKey` - * entries. Based on the fact that a `struct GNUNET_PeerIdentity` is also - * an EdDSA public key. - */ -static struct GNUNET_CONTAINER_MultiPeerMap *esign_keys; - -/** - * Stores the latest generation of our key state. - */ -static struct DH_KeyStateHandle *key_state; - - -/** - * Add the headers we want to set for every /keys response. - * - * @param cls the key state to use - * @param[in,out] response the response to modify - */ -static void -setup_general_response_headers (void *cls, - struct MHD_Response *response) -{ - struct DH_KeyStateHandle *ksh = cls; - char dat[128]; - - TALER_MHD_add_global_headers (response, - true); - 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)); - /* 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")); -} - - -/** - * Initialize @a ksh using the given values for @a signkeys, - * and @a denoms. - * - * @param[in,out] ksh key state handle we build @a ksh for - * @param[in,out] signkeys list of sign keys to return - * @param[in,out] donation_units list of grouped denominations to return - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -create_keys_response (struct DH_KeyStateHandle *ksh, - json_t *signkeys, - json_t *donation_units) -{ - json_t *keys; - char *keys_json; - void *keys_jsonz; - size_t keys_jsonz_size; - int comp; - char etag[sizeof (struct GNUNET_HashCode) * 2]; - - GNUNET_assert (NULL != signkeys); - GNUNET_assert (NULL != donation_units); - GNUNET_assert (NULL != DH_currency); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Creating /keys response\n"); - - 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_array_incref ("signkeys", - signkeys), - GNUNET_JSON_pack_array_incref ("donation_units", - donation_units)); - GNUNET_assert (NULL != keys); - - /* 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 */ - ksh->response_uncompressed - = MHD_create_response_from_buffer (keys_jsonz_size, - keys_json, - MHD_RESPMEM_MUST_FREE); - GNUNET_assert (NULL != ksh->response_uncompressed); - setup_general_response_headers (ksh, - ksh->response_uncompressed); - GNUNET_break (MHD_YES == - MHD_add_response_header (ksh->response_uncompressed, - MHD_HTTP_HEADER_ETAG, - etag)); - /* Also compute compressed version of /keys response */ - comp = TALER_MHD_body_compress (&keys_jsonz, - &keys_jsonz_size); - ksh->response_compressed - = MHD_create_response_from_buffer (keys_jsonz_size, - keys_jsonz, - MHD_RESPMEM_MUST_FREE); - GNUNET_assert (NULL != ksh->response_compressed); - /* If the response is actually compressed, set the - respective header. */ - GNUNET_assert ( (MHD_YES != comp) || - (MHD_YES == - MHD_add_response_header (ksh->response_compressed, - MHD_HTTP_HEADER_CONTENT_ENCODING, - "deflate")) ); - setup_general_response_headers (ksh, - ksh->response_compressed); - /* Set cache control headers: our response varies depending on these headers */ - GNUNET_break (MHD_YES == - MHD_add_response_header (ksh->response_compressed, - 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 (ksh->response_compressed, - MHD_HTTP_HEADER_CACHE_CONTROL, - "public,max-age=86400")); - GNUNET_break (MHD_YES == - MHD_add_response_header (ksh->response_compressed, - MHD_HTTP_HEADER_ETAG, - etag)); - ksh->etag = GNUNET_strdup (etag); - return GNUNET_OK; -} - - -/** - * Closure for #insert_donation_unit_cb and #add_signkey_cb. - */ -struct KeysBuilderContext -{ - - /** - * Array of donation unit keys. - */ - json_t *donation_units; - - /** - * Array of signing keys. - */ - json_t *signkeys; - -}; - -/** - * Function called for all signing keys, used to build up the - * respective JSON response. - * - * @param cls a `struct KeysBuilderContext *` with the array to append keys to - * @param pid the donau public key (in type disguise) - * @param value a `struct SigningKey` - * @return #GNUNET_OK (continue to iterate) - */ -static enum GNUNET_GenericReturnValue -add_sign_key_cb (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct KeysBuilderContext *ctx = cls; - struct SigningKey *sk = value; - - (void) pid; - GNUNET_assert ( - 0 == - json_array_append_new ( - ctx->signkeys, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_timestamp ("stamp_start", - sk->meta.valid_from), - GNUNET_JSON_pack_timestamp ("stamp_expire", - sk->meta.expire_sign), - GNUNET_JSON_pack_data_auto ("key", - &sk->donau_pub)))); - return GNUNET_OK; -} - - -/** - * Function called on all of our current and future donation unit keys - * known to the helper process. Filters out those that are current - * and adds the remaining donation unit keys (with their configuration - * data) to the JSON array. - * - * @param cls the `struct KeysBuilderContext *` - * @param h_du_pub hash of the donation unit public key - * @param value a `struct DH_DonationUnitKey` - * @return #GNUNET_OK (continue to iterate) - */ -static enum GNUNET_GenericReturnValue -insert_donation_unit_cb (void *cls, - const struct GNUNET_HashCode *h_du_pub, - void *value) -{ - struct KeysBuilderContext *kbc = cls; - struct DH_DonationUnitKey *du = value; - - (void) h_du_pub; - GNUNET_assert ( - 0 == json_array_append_new ( - kbc->donation_units, - GNUNET_JSON_PACK ( - DONAU_JSON_pack_donation_unit_pub ("donation_unit_pub", - &du->donation_unit_pub), - GNUNET_JSON_pack_uint64 ("year", - du->validity_year), - GNUNET_JSON_pack_bool ("lost", - du->lost), - TALER_JSON_pack_amount ("value", - &du->value) - ))); - 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; - struct KeysBuilderContext kbc; - - kbc.signkeys = json_array (); - GNUNET_assert (NULL != kbc.signkeys); - kbc.donation_units = json_array (); - GNUNET_assert (NULL != kbc.donation_units); - GNUNET_CONTAINER_multipeermap_iterate (esign_keys, - &add_sign_key_cb, - &kbc); - - if (0 == json_array_size (kbc.signkeys)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "No online signing keys available. Refusing to generate /keys response.\n"); - ret = GNUNET_NO; - goto CLEANUP; - } - GNUNET_CONTAINER_multihashmap_iterate (du_keys, - &insert_donation_unit_cb, - &kbc); - - if (0 == json_array_size (kbc.donation_units)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "No donation units available. Refusing to generate /keys response.\n"); - ret = GNUNET_NO; - goto CLEANUP; - } - - if (GNUNET_OK != - create_keys_response (ksh, - kbc.signkeys, - kbc.donation_units)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to generate key response data\n"); - goto CLEANUP; - } - - ret = GNUNET_OK; - -CLEANUP: - if (NULL != kbc.donation_units) - json_decref (kbc.donation_units); - if (NULL != kbc.signkeys) - json_decref (kbc.signkeys); - return ret; -} - - -/** - * Free donation unit key data. - * - * @param cls a `struct DH_KeyStateHandle`, unused - * @param h_donation_unit_pub hash of the donation unit public key, unused - * @param value a `struct DH_DonationUnitKey` to free - * @return #GNUNET_OK (continue to iterate) - */ -static enum GNUNET_GenericReturnValue -clear_donation_unit_cb (void *cls, - const struct GNUNET_HashCode *h_du_pub, - void *value) -{ - struct DH_DonationUnitKey *dk = value; - - (void) cls; - (void) h_du_pub; - DONAU_donation_unit_pub_free (&dk->donation_unit_pub); - GNUNET_free (dk); - return GNUNET_OK; -} - - -/** - * Free donation unit key data. - * - * @param cls a `struct DH_KeyStateHandle`, unused - * @param pid the online signing key (type-disguised), unused - * @param value a `struct SigningKey` to free - * @return #GNUNET_OK (continue to iterate) - */ -static enum GNUNET_GenericReturnValue -clear_signkey_cb (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct SigningKey *sk = value; - - (void) cls; - (void) pid; - GNUNET_free (sk); - return GNUNET_OK; -} - - -/** - * Synchronize helper state. Polls the key helper for updates. - */ -static void -sync_key_helpers (void) -{ - TALER_CRYPTO_helper_rsa_poll (rsadh); - TALER_CRYPTO_helper_cs_poll (csdh); - TALER_CRYPTO_helper_esign_poll (esh); -} - - -/** - * Check that the given RSA security module's public key is the one - * we have pinned. If it does not match, we die hard. - * - * @param sm_pub RSA security module public key to check - */ -static void -check_donation_unit_rsa_sm_pub (const struct - TALER_SecurityModulePublicKeyP *sm_pub) -{ - if (0 != - GNUNET_memcmp (sm_pub, - &donation_unit_rsa_sm_pub)) - { - if (! GNUNET_is_zero (&donation_unit_rsa_sm_pub)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Our RSA security module changed its key. This must not happen.\n") - ; - GNUNET_assert (0); - } - donation_unit_rsa_sm_pub = *sm_pub; /* TOFU ;-) */ - } -} - - -/** - * Check that the given CS security module's public key is the one - * we have pinned. If it does not match, we die hard. - * - * @param sm_pub RSA security module public key to check - */ -static void -check_donation_unit_cs_sm_pub (const struct - TALER_SecurityModulePublicKeyP *sm_pub) -{ - if (0 != - GNUNET_memcmp (sm_pub, - &donation_unit_cs_sm_pub)) - { - if (! GNUNET_is_zero (&donation_unit_cs_sm_pub)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Our CS security module changed its key. This must not happen.\n") - ; - GNUNET_assert (0); - } - donation_unit_cs_sm_pub = *sm_pub; /* TOFU ;-) */ - } -} - - -/** - * Check that the given EdDSA security module's public key is the one - * we have pinned. If it does not match, we die hard. - * - * @param sm_pub EdDSA security module public key to check - */ -static void -check_esign_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub) -{ - if (0 != - GNUNET_memcmp (sm_pub, - &esign_sm_pub)) - { - if (! GNUNET_is_zero (&esign_sm_pub)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Our EdDSA security module changed its key. This must not happen.\n") - ; - GNUNET_assert (0); - } - esign_sm_pub = *sm_pub; /* TOFU ;-) */ - } -} - - -void -DH_keys_finished () -{ - if (NULL != rsadh) - { - TALER_CRYPTO_helper_rsa_disconnect (rsadh); - rsadh = NULL; - } - if (NULL != csdh) - { - TALER_CRYPTO_helper_cs_disconnect (csdh); - csdh = NULL; - } - if (NULL != esh) - { - TALER_CRYPTO_helper_esign_disconnect (esh); - esh = NULL; - } - if (NULL != du_keys) - { - GNUNET_CONTAINER_multihashmap_iterate (du_keys, - &clear_donation_unit_cb, - NULL); - GNUNET_CONTAINER_multihashmap_destroy (du_keys); - du_keys = NULL; - } - if (NULL != esign_keys) - { - GNUNET_CONTAINER_multipeermap_iterate (esign_keys, - &clear_signkey_cb, - NULL); - GNUNET_CONTAINER_multipeermap_destroy (esign_keys); - esign_keys = NULL; - } -} - - -static void -destroy_key_state (struct DH_KeyStateHandle *ksh) -{ - if (NULL != ksh->response_compressed) - MHD_destroy_response (ksh->response_compressed); - if (NULL != ksh->response_uncompressed) - MHD_destroy_response (ksh->response_uncompressed); - GNUNET_free (ksh->etag); - GNUNET_free (ksh); -} - - -/** - * 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 NULL - * @param section_name name of the donation_unit 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 donation_unit_pub that is available (or was purged) - * @param bs_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 - */ -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, - struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub, - const struct TALER_SecurityModulePublicKeyP *sm_pub, - const struct TALER_SecurityModuleSignatureP *sm_sig) -{ - struct DH_DonationUnitKey *du; - struct TALER_Amount value; - enum GNUNET_DB_QueryStatus qs; - - (void) cls; - (void) sm_sig; /* not using offline signatures */ - GNUNET_assert (GNUNET_CRYPTO_BSA_RSA == bs_pub->cipher); - if (GNUNET_OK != - TALER_config_get_amount (DH_cfg, - section_name, - "value", - &value)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "RSA helper provided key for configuration section `%s' that has no `value' option set\n", - section_name); - return; - } - /* FIXME: could additionally sanity-check that this - section actually has CIPHER = RSA, etc. */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "RSA helper announces key %s for donation_unit type %s with validity %s\n", - GNUNET_h2s (&h_rsa->hash), - section_name, - GNUNET_STRINGS_relative_time_to_string (validity_duration, - false)); - du = GNUNET_CONTAINER_multihashmap_get (du_keys, - &h_rsa->hash); - if (NULL != du) - { - /* only update 'lost' status */ - du->lost = GNUNET_TIME_relative_is_zero (validity_duration); - return; - } - GNUNET_assert (NULL != sm_pub); - check_donation_unit_rsa_sm_pub (sm_pub); - - du = GNUNET_new (struct DH_DonationUnitKey); - du->h_donation_unit_pub.hash = h_rsa->hash; - du->donation_unit_pub.bsign_pub_key - = GNUNET_CRYPTO_bsign_pub_incref (bs_pub); - du->validity_year = GNUNET_TIME_time_to_year (start_time.abs_time); - du->value = value; - du->lost = GNUNET_TIME_relative_is_zero (validity_duration); - - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multihashmap_put ( - du_keys, - &du->h_donation_unit_pub.hash, - du, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - - qs = DH_plugin->insert_donation_unit ( - DH_plugin->cls, - &du->h_donation_unit_pub, - &du->donation_unit_pub, - du->validity_year, - &du->value); - if (qs < 0) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to insert donation units\n"); - GNUNET_SCHEDULER_shutdown (); - DH_global_ret = EXIT_FAILURE; - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Inserted RSA donation unit of %s\n", - TALER_amount2s (&value)); - key_generation++; -} - - -/** - * 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 NULL - * @param section_name name of the donation unit 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 donation_unit_pub that is available (or was purged) - * @param sm_pub the public key itself, NULL if the key was revoked or purged - * @param sm_pub signature by the security module - */ -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, - struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub, - const struct TALER_SecurityModulePublicKeyP *sm_pub, - const struct TALER_SecurityModuleSignatureP *sm_sig) -{ - struct DH_DonationUnitKey *du; - struct TALER_Amount value; - enum GNUNET_DB_QueryStatus qs; - - (void) cls; - (void) sm_sig; /* we are not using offline signatures */ - GNUNET_assert (GNUNET_CRYPTO_BSA_CS == bs_pub->cipher); - if (GNUNET_OK != - TALER_config_get_amount (DH_cfg, - section_name, - "value", - &value)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "CS helper provided key for configuration section `%s' that has no `value' option set\n", - section_name); - return; - } - /* FIXME: could additionally sanity-check that this - section actually has CIPHER = CS, etc. */ - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "CS helper announces key %s for donation unit type %s with validity %s\n", - GNUNET_h2s (&h_cs->hash), - section_name, - GNUNET_STRINGS_relative_time_to_string (validity_duration, - false)); - du = GNUNET_CONTAINER_multihashmap_get (du_keys, - &h_cs->hash); - if (NULL != du) - { - /* should be just an update (revocation!), so update existing entry */ - du->lost = GNUNET_TIME_relative_is_zero (validity_duration); - return; - } - GNUNET_assert (NULL != sm_pub); - check_donation_unit_cs_sm_pub (sm_pub); - - du = GNUNET_new (struct DH_DonationUnitKey); - du->h_donation_unit_pub.hash = h_cs->hash; - du->donation_unit_pub.bsign_pub_key - = GNUNET_CRYPTO_bsign_pub_incref (bs_pub); - du->validity_year = GNUNET_TIME_time_to_year (start_time.abs_time); - du->value = value; - du->lost = GNUNET_TIME_relative_is_zero (validity_duration); - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multihashmap_put ( - du_keys, - &du->h_donation_unit_pub.hash, - du, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - - qs = DH_plugin->insert_donation_unit ( - DH_plugin->cls, - &du->h_donation_unit_pub, - &du->donation_unit_pub, - du->validity_year, - &du->value); - if (qs < 0) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to insert donation units\n"); - GNUNET_SCHEDULER_shutdown (); - DH_global_ret = EXIT_FAILURE; - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Inserted CS donation unit of %s\n", - TALER_amount2s (&value)); - - key_generation++; -} - - -/** - * 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 NULL - * @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 donau_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 - */ -static void -helper_esign_cb ( - void *cls, - struct GNUNET_TIME_Timestamp start_time, - struct GNUNET_TIME_Relative validity_duration, - const struct TALER_ExchangePublicKeyP *donau_pub, - const struct TALER_SecurityModulePublicKeyP *sm_pub, - const struct TALER_SecurityModuleSignatureP *sm_sig) -{ - struct SigningKey *sk; - struct GNUNET_PeerIdentity pid; - unsigned long long expire_legal; - /* need to "cast" because secmod works with TALER_ExchangePublicKeyP */ - struct DONAU_DonauPublicKeyP donau_pubkey = { - .eddsa_pub = donau_pub->eddsa_pub - }; - enum GNUNET_DB_QueryStatus qs; - - (void) cls; - (void) sm_sig; /* not using offline signing */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "EdDSA helper announces signing key %s with validity %s\n", - TALER_B2S (donau_pub), - GNUNET_STRINGS_relative_time_to_string (validity_duration, - false)); - - pid.public_key = donau_pub->eddsa_pub; - sk = GNUNET_CONTAINER_multipeermap_get (esign_keys, - &pid); - if (NULL != sk) - { - /* should be just an update (revocation!), so update existing entry */ - sk->lost = GNUNET_TIME_relative_is_zero (validity_duration); - return; - } - GNUNET_assert (NULL != sm_pub); - check_esign_sm_pub (sm_pub); - - sk = GNUNET_new (struct SigningKey); - sk->donau_pub = donau_pubkey; - sk->meta.valid_from = start_time; - sk->meta.expire_sign - = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add (start_time.abs_time, - validity_duration)); - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (DH_cfg, - "donau", - "EXPIRE_LEGAL_YEARS", - &expire_legal)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Need EXPIRE_LEGAL_YEARS in section `donau'\n"); - GNUNET_SCHEDULER_shutdown (); - DH_global_ret = EXIT_FAILURE; - return; - } - sk->meta.expire_legal - = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add (start_time.abs_time, - GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_YEARS, - expire_legal))); - - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multipeermap_put ( - esign_keys, - &pid, - sk, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - - qs = DH_plugin->insert_signing_key ( - DH_plugin->cls, - &donau_pubkey, - &sk->meta); - if (qs < 0) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to insert donation units\n"); - GNUNET_SCHEDULER_shutdown (); - DH_global_ret = EXIT_FAILURE; - return; - } - - key_generation++; -} - - -enum GNUNET_GenericReturnValue -DH_keys_init () -{ - du_keys - = GNUNET_CONTAINER_multihashmap_create (1024, - GNUNET_YES); - esign_keys - = GNUNET_CONTAINER_multipeermap_create (32, - GNUNET_NO /* MUST BE NO! */); - rsadh = TALER_CRYPTO_helper_rsa_connect (DH_cfg, - "donau", - &helper_rsa_cb, - NULL); - if (NULL == rsadh) - { - GNUNET_break (0); - DH_keys_finished (); - return GNUNET_SYSERR; - } - csdh = TALER_CRYPTO_helper_cs_connect (DH_cfg, - "donau", - &helper_cs_cb, - NULL); - if (NULL == csdh) - { - GNUNET_break (0); - DH_keys_finished (); - return GNUNET_SYSERR; - } - esh = TALER_CRYPTO_helper_esign_connect (DH_cfg, - "donau", - &helper_esign_cb, - NULL); - if (NULL == esh) - { - GNUNET_break (0); - DH_keys_finished (); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Function called with information about the donau's donation_unit keys. - * - * @param cls NULL - * @param donation_unit_pub public key of the donation_unit - * @param h_donation_unit_pub hash of @a donation_unit_pub - * @param validity_year of the donation unit - * @param value of the donation unit - */ -static enum GNUNET_GenericReturnValue -donation_unit_info_cb ( - void *cls, - const struct DONAU_DonationUnitHashP *h_donation_unit_pub, - const struct DONAU_DonationUnitPublicKey *donation_unit_pub, - uint64_t validity_year, - struct TALER_Amount *value) -{ - struct DH_DonationUnitKey *du; - - (void) cls; - GNUNET_assert (GNUNET_CRYPTO_BSA_INVALID != - donation_unit_pub->bsign_pub_key->cipher); - du = GNUNET_CONTAINER_multihashmap_get (du_keys, - &h_donation_unit_pub->hash); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Got %s key from database\n", - NULL == du ? "unknown" : "known"); - if (NULL != du) - { - /* we already know this, nothing to do */ - return GNUNET_OK; - } - - du = GNUNET_new (struct DH_DonationUnitKey); - du->h_donation_unit_pub = *h_donation_unit_pub; - DONAU_donation_unit_pub_deep_copy (&du->donation_unit_pub, - donation_unit_pub); - du->validity_year = validity_year; - du->value = *value; - du->lost = true; /* no private key known, that can only come from the helper! */ - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multihashmap_put (du_keys, - &du->h_donation_unit_pub.hash, - du, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) - ); - return GNUNET_OK; -} - - -/** - * Function called with information about the donau's online signing keys. - * - * @param cls NULL - * @param donau_pub the public key - * @param meta meta data information about the denomination type (expirations) - */ -static void -iterate_active_signing_keys_cb ( - void *cls, - const struct DONAU_DonauPublicKeyP *donau_pub, - struct DONAUDB_SignkeyMetaData *meta) -{ - /* The 'pid' is used as the key in the "peer" map... */ - struct GNUNET_PeerIdentity pid = { - .public_key = donau_pub->eddsa_pub - }; - struct SigningKey *sk; - - (void) cls; - sk = GNUNET_CONTAINER_multipeermap_get (esign_keys, - &pid); - if (NULL != sk) - { - /* should be just an update (revocation!), so update existing entry */ - return; - } - sk = GNUNET_new (struct SigningKey); - sk->donau_pub = *donau_pub; - sk->meta = *meta; - sk->lost = true; /* no private key known, that can only come from the helper! */ - GNUNET_assert ( - GNUNET_OK == - GNUNET_CONTAINER_multipeermap_put (esign_keys, - &pid, - sk, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) - ); -} - - -/** - * Create a key state. - * - * @return NULL on error (i.e. failed to access database) - */ -static struct DH_KeyStateHandle * -build_key_state () -{ - 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; - - /* NOTE: fetches master-signed signkeys, but ALSO those that were revoked! */ - GNUNET_break (GNUNET_OK == - DH_plugin->preflight (DH_plugin->cls)); - qs = DH_plugin->iterate_donation_units (DH_plugin->cls, - &donation_unit_info_cb, - NULL); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Fetched %d donation unit keys from DB\n", - (int) qs); - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); - destroy_key_state (ksh); - return NULL; - } - - /* NOTE: ONLY fetches active signkeys! */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Fetching active signing keys from DB\n"); - qs = DH_plugin->iterate_active_signing_keys (DH_plugin->cls, - &iterate_active_signing_keys_cb, - NULL); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Fetched %d active signing keys from DB\n", - (int) qs); - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); - destroy_key_state (ksh); - return NULL; - } - - 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); - return NULL; - } - - return ksh; -} - - -static struct DH_KeyStateHandle* -DH_keys_get_state () -{ - struct DH_KeyStateHandle *old_ksh; - struct DH_KeyStateHandle *ksh; - - old_ksh = key_state; - if (NULL == old_ksh) - { - ksh = build_key_state (); - 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 (); - key_state = ksh; - destroy_key_state (old_ksh); - return ksh; - } - return old_ksh; -} - - -MHD_RESULT -DH_handler_keys (struct DH_RequestContext *rc, - const char *const args[]) -{ - struct MHD_Connection *connection = rc->connection; - struct DH_KeyStateHandle *ksh; - struct MHD_Response *resp; - - (void) args; - sync_key_helpers (); - ksh = DH_keys_get_state (); - if (NULL == ksh) - { - if ( (GNUNET_is_zero (&donation_unit_rsa_sm_pub)) && - (GNUNET_is_zero (&donation_unit_cs_sm_pub)) ) - { - /* Either IPC failed, or neither helper had any donation_unit configured. */ - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_GATEWAY, - TALER_EC_DONAU_DONATION_UNIT_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_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to build /keys response?\n"); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_SERVICE_UNAVAILABLE, - TALER_EC_DONAU_GENERIC_KEYS_MISSING, - "failed to create keys response"); - } - resp = (TALER_MHD_CT_DEFLATE == - TALER_MHD_can_compress (connection, - TALER_MHD_CT_DEFLATE)) - ? ksh->response_compressed - : ksh->response_uncompressed; - GNUNET_assert (NULL != resp); - return MHD_queue_response (connection, - MHD_HTTP_OK, - resp); - -} - - -enum TALER_ErrorCode -DH_keys_donau_sign_ ( - const struct GNUNET_CRYPTO_SignaturePurpose *purpose, - struct DONAU_DonauPublicKeyP *pub, - struct DONAU_DonauSignatureP *sig) -{ - struct DH_KeyStateHandle *ksh; - enum TALER_ErrorCode ec; - - ksh = DH_keys_get_state (); - if (NULL == ksh) - { - /* This *can* happen if the Donau's crypto helper is not running - or had some bad error. */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Cannot sign request, no valid signing keys available.\n"); - return TALER_EC_DONAU_GENERIC_KEYS_MISSING; - } - - { - /* need to "cast" because TALER_CRYPTO works with TALER_Exchange.. */ - struct TALER_ExchangePublicKeyP donau_pub; - struct TALER_ExchangeSignatureP donau_sig; - - ec = TALER_CRYPTO_helper_esign_sign_ (esh, - purpose, - &donau_pub, - &donau_sig); - if (TALER_EC_NONE != ec) - return ec; - pub->eddsa_pub = donau_pub.eddsa_pub; - sig->eddsa_sig = donau_sig.eddsa_signature; - } - return ec; -} - - -enum TALER_ErrorCode -DH_keys_donation_unit_batch_sign ( - unsigned int num_bkps, - const struct DONAU_BkpSignData bkps[num_bkps], - struct DONAU_BlindedDonationUnitSignature du_sigs[num_bkps]) -{ - struct DH_KeyStateHandle *ksh; - struct DH_DonationUnitKey *du; - struct TALER_CRYPTO_RsaSignRequest rsrs[num_bkps]; - struct TALER_CRYPTO_CsSignRequest csrs[num_bkps]; - struct TALER_BlindedDenominationSignature rs[num_bkps]; - struct TALER_BlindedDenominationSignature cs[num_bkps]; - unsigned int rsrs_pos = 0; - unsigned int csrs_pos = 0; - enum TALER_ErrorCode ec; - - ksh = DH_keys_get_state (); - if (NULL == ksh) - return TALER_EC_DONAU_GENERIC_KEYS_MISSING; - for (unsigned int i = 0; i<num_bkps; i++) - { - const struct DONAU_DonationUnitHashP *h_du_pub = - bkps[i].h_donation_unit_pub; - const struct DONAU_BlindedUniqueDonorIdentifier *budi = bkps[i].budi; - - du = GNUNET_CONTAINER_multihashmap_get (du_keys, - &h_du_pub->hash); - if (NULL == du) - return TALER_EC_DONAU_GENERIC_DONATION_UNIT_UNKNOWN; - if (budi->blinded_message->cipher != - du->donation_unit_pub.bsign_pub_key->cipher) - return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; - switch (du->donation_unit_pub.bsign_pub_key->cipher) - { - case GNUNET_CRYPTO_BSA_RSA: - /* See DONAU_donation_unit_pub_hash: we guarantee that these - hashes are equivalent! */ - rsrs[rsrs_pos].h_rsa - = (const struct TALER_RsaPubHashP *) &du->h_donation_unit_pub; - rsrs[rsrs_pos].msg - = budi->blinded_message->details.rsa_blinded_message.blinded_msg; - rsrs[rsrs_pos].msg_size - = budi->blinded_message->details.rsa_blinded_message.blinded_msg_size; - rsrs_pos++; - break; - case GNUNET_CRYPTO_BSA_CS: - /* See DONAU_donation_unit_pub_hash: we guarantee that these - hashes are equivalent! */ - csrs[csrs_pos].h_cs - = (const struct TALER_CsPubHashP *) &du->h_donation_unit_pub; - csrs[csrs_pos].blinded_planchet - = &budi->blinded_message->details.cs_blinded_message; - csrs_pos++; - break; - default: - return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; - } - } - - if ( (0 != csrs_pos) && - (0 != rsrs_pos) ) - { - memset (rs, - 0, - sizeof (rs)); - memset (cs, - 0, - sizeof (cs)); - } - ec = TALER_EC_NONE; - if (0 != csrs_pos) - { - ec = TALER_CRYPTO_helper_cs_batch_sign ( - csdh, - csrs_pos, - csrs, - false, // for_melt - cs); - if (TALER_EC_NONE != ec) - { - for (unsigned int i = 0; i<csrs_pos; i++) - { - if (NULL != cs[i].blinded_sig) - { - GNUNET_CRYPTO_blinded_sig_decref (cs[i].blinded_sig); - cs[i].blinded_sig = NULL; - } - } - return ec; - } - } - if (0 != rsrs_pos) - { - ec = TALER_CRYPTO_helper_rsa_batch_sign ( - rsadh, - rsrs_pos, - rsrs, - rs); - if (TALER_EC_NONE != ec) - { - for (unsigned int i = 0; i<csrs_pos; i++) - { - if (NULL != cs[i].blinded_sig) - { - GNUNET_CRYPTO_blinded_sig_decref (cs[i].blinded_sig); - cs[i].blinded_sig = NULL; - } - } - for (unsigned int i = 0; i<rsrs_pos; i++) - { - if (NULL != rs[i].blinded_sig) - { - GNUNET_CRYPTO_blinded_sig_decref (rs[i].blinded_sig); - rs[i].blinded_sig = NULL; - } - } - return ec; - } - } - - rsrs_pos = 0; - csrs_pos = 0; - for (unsigned int i = 0; i<num_bkps; i++) - { - const struct DONAU_BlindedUniqueDonorIdentifier *budi = bkps[i].budi; - - switch (budi->blinded_message->cipher) - { - case GNUNET_CRYPTO_BSA_RSA: - du_sigs[i].blinded_sig = rs[rsrs_pos++].blinded_sig; - break; - case GNUNET_CRYPTO_BSA_CS: - du_sigs[i].blinded_sig = cs[csrs_pos++].blinded_sig; - break; - default: - GNUNET_assert (0); - } - } - return TALER_EC_NONE; -} - - -struct DH_DonationUnitKey * -DH_keys_donation_unit_by_hash ( - const struct DONAU_DonationUnitHashP *h_du_pub) -{ - return GNUNET_CONTAINER_multihashmap_get (du_keys, - &h_du_pub->hash); -} - - -enum TALER_ErrorCode -DH_keys_donation_unit_cs_r_pub ( - const struct DONAU_DonationUnitHashP *h_donation_unit_pub, - const struct GNUNET_CRYPTO_CsSessionNonce *nonce, - struct GNUNET_CRYPTO_CSPublicRPairP *r_pub) -{ - struct DH_DonationUnitKey *dk; - - dk = DH_keys_donation_unit_by_hash (h_donation_unit_pub); - if (NULL == dk) - { - return TALER_EC_DONAU_GENERIC_DONATION_UNIT_UNKNOWN; - } - if (GNUNET_CRYPTO_BSA_CS != - dk->donation_unit_pub.bsign_pub_key->cipher) - { - return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; - } - - { - struct TALER_CRYPTO_CsDeriveRequest cdr = { - .h_cs = (const struct TALER_CsPubHashP *) &dk->h_donation_unit_pub, - .nonce = nonce - }; - return TALER_CRYPTO_helper_cs_r_derive (csdh, - &cdr, - false, - r_pub); - } -} - - -/* end of donau-httpd_keys.c */ diff --git a/src/donau/donau-httpd_keys.h b/src/donau/donau-httpd_keys.h @@ -1,207 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2023-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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file donau-httpd_keys.h - * @brief management of our various keys - * @author Christian Grothoff - */ -#include <donau_config.h> -#include <taler/taler_json_lib.h> -#include <taler/taler_mhd_lib.h> -#include "donau_util.h" -#include "donaudb_plugin.h" -#include "donau-httpd.h" - -#ifndef DONAU_HTTPD_KEYS_H -#define DONAU_HTTPD_KEYS_H - - -/** - * @brief All information about a donation unit key (which is used to - * sign donation receipts into existence). - */ -struct DH_DonationUnitKey -{ - - /** - * Hash code of the donation unit public key. - */ - struct DONAU_DonationUnitHashP h_donation_unit_pub; - - /** - * Decoded donation unit public key (the hash of it is in - * @e issue, but we sometimes need the full public key as well). - */ - struct DONAU_DonationUnitPublicKey donation_unit_pub; - - /** - * The validity year. - */ - uint64_t validity_year; - - /** - * Value that the donation unit represents. - */ - struct TALER_Amount value; - - /** - * Did we lose the private keys? - */ - bool lost; - -}; - -/** - * Information needed to create a blind signature. - */ -struct DH_BlindSignData -{ - /** - * Hash of key to sign with. - */ - const struct DONAU_DonationUnitHashP *h_du_pub; - - /** - * Blinded planchet to sign over. - */ - const struct DONAU_BlindedUniqueDonorIdentifier *budi; -}; - -/** - * Sign the message in @a purpose with the doanu's signing key. - * - * The @a purpose data is the beginning of the data of which the signature is - * to be created. The `size` field in @a purpose must correctly indicate the - * number of bytes of the data structure, including its header. Use - * #DH_keys_doanu_sign() instead of calling this function directly! - * - * @param purpose the message to sign - * @param[out] pub set to the current public signing key of the doanu - * @param[out] sig signature over purpose using current signing key - * @return #TALER_EC_NONE on success - */ -enum TALER_ErrorCode -DH_keys_donau_sign_ ( - const struct GNUNET_CRYPTO_SignaturePurpose *purpose, - struct DONAU_DonauPublicKeyP *pub, - struct DONAU_DonauSignatureP *sig); - -/** - * @ingroup crypto - * @brief EdDSA sign a given block. - * - * The @a ps data must be a fixed-size struct for which the signature is to be - * created. The `size` field in @a ps->purpose must correctly indicate the - * number of bytes of the data structure, including its header. - * - * @param ps packed struct with what to sign, MUST begin with a purpose - * @param[out] pub where to store the public key to use for the signing - * @param[out] sig where to write the signature - * @return #TALER_EC_NONE on success - */ -#define DH_keys_donau_sign(ps,pub,sig) \ - ({ \ - /* check size is set correctly */ \ - GNUNET_assert (htonl ((ps)->purpose.size) == \ - sizeof (*ps)); \ - /* check 'ps' begins with the purpose */ \ - GNUNET_static_assert (((void*) (ps)) == \ - ((void*) &(ps)->purpose)); \ - DH_keys_donau_sign_ (&(ps)->purpose, \ - pub, \ - sig); \ - }) - -/** - * Resumes all suspended /keys requests, we may now have key material - * (or are shutting down). - * - * @param do_shutdown are we shutting down? - */ -void -TEH_resume_keys_requests (bool do_shutdown); - - -/** - * Function to call to handle requests to "/keys" by sending - * back our current key material. - * - * @param rc request context - * @param args array of additional options (must be empty for this function) - * @return MHD result code - */ -MHD_RESULT -DH_handler_keys (struct DH_RequestContext *rc, - const char *const args[]); - - -/** - * Look up the issue for a donation unit public key. - * - * @param h_du_pub hash of donation unit public key - * @return the donation unit key issue, - * or NULL if @a h_du_pub could not be found - */ -struct DH_DonationUnitKey * -DH_keys_donation_unit_by_hash ( - const struct DONAU_DonationUnitHashP *h_du_pub); - - -/** - * Initialize keys subsystem. - * - * @return #GNUNET_OK on success - */ -enum GNUNET_GenericReturnValue -DH_keys_init (void); - - -/** - * Fully clean up keys subsystem. - */ -void -DH_keys_finished (void); - - -/** - * Request to sign @a budis. - * - * @param num_bkps length of @a budis array - * @param bkps array with data to blindly sign (and keys to sign with) - * @param[out] du_sigs array set to the blind signature on success; must be of length @a num_bkps - * @return #TALER_EC_NONE on success - */ -enum TALER_ErrorCode -DH_keys_donation_unit_batch_sign ( - unsigned int num_bkps, - const struct DONAU_BkpSignData bkps[num_bkps], - struct DONAU_BlindedDonationUnitSignature du_sigs[num_bkps]); - -/** - * Request to derive CS @a r_pub using the donation_unit and nonce from @a cdd. - * - * @param h_donation_unit_pub hash to compute @a r_pub from - * @param nonce - * @param[out] r_pub where to write the result - * @return #TALER_EC_NONE on success - */ -enum TALER_ErrorCode -DH_keys_donation_unit_cs_r_pub ( - const struct DONAU_DonationUnitHashP *h_donation_unit_pub, - const struct GNUNET_CRYPTO_CsSessionNonce *nonce, - struct GNUNET_CRYPTO_CSPublicRPairP *r_pub); - -#endif diff --git a/src/donau/donau-httpd_patch-charities-CHARITY_ID.c b/src/donau/donau-httpd_patch-charities-CHARITY_ID.c @@ -0,0 +1,162 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_patch-charities-CHARITY_ID.c + * @brief Handle request to update a charity entry. + * @author Bohdan Potuzhnyi + */ +#include <donau_config.h> +#include <gnunet/gnunet_json_lib.h> +#include <gnunet/gnunet_util_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include <taler/taler_json_lib.h> +#include <taler/taler_mhd_lib.h> +#include <taler/taler_util.h> +#include "donau-httpd_patch-charities-CHARITY_ID.h" +#include "donau-httpd_db.h" + + +MHD_RESULT +DH_handler_patch_charities (struct DH_RequestContext *rc, + const json_t *root, + const char *const args[]) +{ + struct DONAU_CharityPublicKeyP charity_pub; + uint64_t charity_id; + char dummy; + const char *charity_name = NULL; + const char *charity_url = NULL; + struct TALER_Amount max_per_year; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("charity_pub", + &charity_pub), + GNUNET_JSON_spec_string ("charity_name", + &charity_name), + GNUNET_JSON_spec_string ("charity_url", + &charity_url), + TALER_JSON_spec_amount ("max_per_year", + DH_currency, + &max_per_year), + GNUNET_JSON_spec_end () + }; + + if ( (NULL == args[0]) || + (1 != sscanf (args[0], + "%lu%c", + &charity_id, + &dummy)) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "charity_id"); + } + + { + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (rc->connection, + root, + spec); + if (GNUNET_SYSERR == res) + return MHD_NO; /* hard failure */ + if (GNUNET_NO == res) + { + GNUNET_break_op (0); + return MHD_YES; /* failure */ + } + } + + { + struct DONAUDB_CharityMetaData meta; + enum GNUNET_DB_QueryStatus qs; + + qs = DH_plugin->lookup_charity (DH_plugin->cls, + charity_id, + &meta); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "lookup_charity"); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_DONAU_CHARITY_NOT_FOUND, + args[0]); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + + if (0 < TALER_amount_cmp (&meta.receipts_to_date, + &max_per_year)) + { + GNUNET_break_op (0); + GNUNET_free (meta.charity_name); + GNUNET_free (meta.charity_url); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "max_per_year must NOT be SMALLER than receipts_to_date"); + } + + qs = DH_plugin->update_charity (DH_plugin->cls, + charity_id, + &charity_pub, + charity_name, + charity_url, + &max_per_year); + GNUNET_free (meta.charity_name); + GNUNET_free (meta.charity_url); + + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "update_charity"); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_DONAU_CHARITY_NOT_FOUND, + args[0]); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + return TALER_MHD_reply_static (rc->connection, + MHD_HTTP_OK, + NULL, + NULL, + 0); + } + } + + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "charity_patch"); +} + + +/* end of donau-httpd_patch-charities-CHARITY_ID.c */ diff --git a/src/donau/donau-httpd_patch-charities-CHARITY_ID.h b/src/donau/donau-httpd_patch-charities-CHARITY_ID.h @@ -0,0 +1,42 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_patch-charities-CHARITY_ID.h + * @brief Handle PATCH /charities/$CHARITY_ID request + * @author Johannes Casaburi + */ +#ifndef DONAU_HTTPD_PATCH_CHARITIES_CHARITY_ID_H +#define DONAU_HTTPD_PATCH_CHARITIES_CHARITY_ID_H + +#include <microhttpd.h> +#include "donau-httpd.h" + + +/** + * Handle a PATCH "/charities/$CHARITY_ID" request. + * + * @param rc request context + * @param root uploaded JSON data with updates + * @param args array with the charity identifier in args[0] + * @return MHD result code + */ +MHD_RESULT +DH_handler_patch_charities ( + struct DH_RequestContext *rc, + const json_t *root, + const char *const args[]); + +#endif diff --git a/src/donau/donau-httpd_post-batch-issue-CHARITY_ID.c b/src/donau/donau-httpd_post-batch-issue-CHARITY_ID.c @@ -0,0 +1,468 @@ +/* + This file is part of TALER + Copyright (C) 2024, 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_post-batch-issue-CHARITY_ID.c + * @brief Handle request to issue receipts. + * @author Lukas Matyja + */ +#include <donau_config.h> +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_json_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include <pthread.h> +#include <taler/taler_json_lib.h> +#include <taler/taler_mhd_lib.h> +#include <taler/taler_signatures.h> +#include "donaudb_plugin.h" +#include "donau-httpd_post-batch-issue-CHARITY_ID.h" +#include "donau-httpd_db.h" +#include "donau_json_lib.h" +#include "donau-httpd_get-keys.h" + + +/** + * Parse a bkp encoded in JSON. + * + * @param[out] bkp where to return the result + * @param bkp_key_obj json to parse + * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if @a bkp_key_obj + * is malformed. + */ +static enum GNUNET_GenericReturnValue +parse_json_bkp (struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkp, + const json_t *bkp_key_obj) +{ + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("h_donation_unit_pub", + &bkp->h_donation_unit_pub), + DONAU_JSON_spec_blinded_donation_identifier ("blinded_udi", + &bkp->blinded_udi), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (bkp_key_obj, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + /* FIXME: Check for duplicate blinded UDIs.*/ + return GNUNET_OK; +} + + +/** + * Free @a bkps array. + * + * @param num_bkps length of the array + * @param[in] bkps array to release + */ +static void +free_bkps (size_t num_bkps, + struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps) +{ + for (unsigned int i = 0; i<num_bkps; i++) + { + struct DONAU_BlindedUniqueDonorIdentifier *budi = &bkps[i].blinded_udi; + + if (NULL != budi->blinded_message) + GNUNET_CRYPTO_blinded_message_decref (budi->blinded_message); + } + GNUNET_free (bkps); +} + + +/** + * Parse signatures to JSON. + * + * @param num_sig number of signatures + * @param signatures Blinded donation unit signatures + * @param[out] j_signatures JSON object + * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if we could not parse + * is malformed. + */ +static void +signatures_to_json (const size_t num_sig, + struct DONAU_BlindedDonationUnitSignature *signatures, + json_t *j_signatures) +{ + for (size_t i = 0; i < num_sig; i++) + { + struct DONAU_BlindedDonationUnitSignature *signature = &signatures[i]; + + GNUNET_assert ( + 0 == json_array_append_new ( + j_signatures, + GNUNET_JSON_PACK ( + DONAU_JSON_pack_blinded_donation_unit_sig ("blinded_signature", + signature)))); + } +} + + +MHD_RESULT +DH_handler_post_batch_issue (struct DH_RequestContext *rc, + const json_t *root, + const char *const args[1]) +{ + struct DONAU_CharitySignatureP charity_sig; + uint64_t year; + bool second_time = false; + unsigned long long charity_id; + char dummy; + const json_t *budikeypairs; + size_t num_bkps; + struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps; + struct DONAUDB_CharityMetaData charity_meta; + json_t *blind_signatures; + struct DONAU_DonationReceiptHashP h_receipts; + struct TALER_Amount receipts_sum; + + if ( (NULL == args[0]) || + (1 != sscanf (args[0], + "%llu%c", + &charity_id, + &dummy)) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "charity_id"); + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "issue receipts for charity id: %llu\n", + charity_id); + + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_array_const ("budikeypairs", + &budikeypairs), + GNUNET_JSON_spec_fixed_auto ("charity_sig", + &charity_sig), + GNUNET_JSON_spec_uint64 ("year", + &year), + GNUNET_JSON_spec_end () + }; + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (rc->connection, + root, + spec); + if (GNUNET_SYSERR == res) + return MHD_NO; /* hard failure */ + if (GNUNET_NO == res) + { + GNUNET_break_op (0); + return MHD_YES; /* failure */ + } + } + + /* parse the budikeypairs array */ + num_bkps = json_array_size (budikeypairs); + if (0 == num_bkps) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "budikeypairs"); + + } + + bkps = GNUNET_new_array (num_bkps, + struct DONAU_BlindedUniqueDonorIdentifierKeyPair); + { + json_t *bkp_obj; + size_t index; + + json_array_foreach (budikeypairs, + index, + bkp_obj) + { + if (GNUNET_SYSERR == + parse_json_bkp (&bkps[index], + bkp_obj)) + { + GNUNET_break_op (0); + free_bkps (num_bkps, + bkps); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "budikeypairs"); + } + } + } + + { + enum GNUNET_DB_QueryStatus qs_charity; + + qs_charity = DH_plugin->lookup_charity (DH_plugin->cls, + charity_id, + &charity_meta); + switch (qs_charity) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break_op (0); + free_bkps (num_bkps, + bkps); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break_op (0); + free_bkps (num_bkps, + bkps); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_DONAU_CHARITY_NOT_FOUND, + NULL); + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + } + + /* verify charity signature */ + if (GNUNET_OK != + DONAU_charity_bkp_verify (num_bkps, + bkps, + &charity_meta.charity_pub, + &charity_sig)) + { + GNUNET_break_op (0); + free_bkps (num_bkps, + bkps); + GNUNET_free (charity_meta.charity_name); + GNUNET_free (charity_meta.charity_url); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_DONAU_CHARITY_SIGNATURE_INVALID, + "charity_sig"); + } + + GNUNET_free (charity_meta.charity_name); + GNUNET_free (charity_meta.charity_url); + { + /* request already made? -> idempotent */ + enum GNUNET_DB_QueryStatus qs_check_receipts; + struct DONAUDB_IssuedReceiptsMetaData check_receipts_meta; + struct GNUNET_HashContext *hc; + + blind_signatures = json_array (); + GNUNET_assert (NULL != blind_signatures); + hc = GNUNET_CRYPTO_hash_context_start (); + for (size_t i = 0; i < num_bkps; i++) + { + const struct GNUNET_CRYPTO_BlindedMessage *bm + = bkps[i].blinded_udi.blinded_message; + + GNUNET_CRYPTO_hash_context_read (hc, + &bkps[i].h_donation_unit_pub, + sizeof (bkps[i].h_donation_unit_pub)); + switch (bm->cipher) + { + case GNUNET_CRYPTO_BSA_INVALID: + GNUNET_assert (0); + break; + case GNUNET_CRYPTO_BSA_CS: + GNUNET_CRYPTO_hash_context_read ( + hc, + &bm->details.cs_blinded_message, + sizeof (bm->details.cs_blinded_message)); + break; + case GNUNET_CRYPTO_BSA_RSA: + GNUNET_CRYPTO_hash_context_read ( + hc, + bm->details.rsa_blinded_message.blinded_msg, + bm->details.rsa_blinded_message.blinded_msg_size); + break; + } + } + GNUNET_CRYPTO_hash_context_read (hc, + &charity_sig, + sizeof (struct DONAU_CharitySignatureP)); + GNUNET_CRYPTO_hash_context_read (hc, + &year, + sizeof (uint64_t)); + GNUNET_CRYPTO_hash_context_finish (hc, + &h_receipts.hash); + +start: + qs_check_receipts + = DH_plugin->lookup_issued_receipts (DH_plugin->cls, + &h_receipts, + &check_receipts_meta); + switch (qs_check_receipts) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + free_bkps (num_bkps, + bkps); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "request has not been made yet (first time)!\n"); + break; /* it's the first request from the charity, we can proceed */ + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "request has been made already!\n"); + signatures_to_json (num_bkps, + check_receipts_meta.blinded_sigs, + blind_signatures); + for (size_t i = 0; i < check_receipts_meta.num_sig; i++) + { + GNUNET_CRYPTO_blinded_sig_decref ( + check_receipts_meta.blinded_sigs[i].blinded_sig); + } + free_bkps (num_bkps, + bkps); + return TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_OK, + TALER_JSON_pack_amount ("issued_amount", + &check_receipts_meta.amount), + GNUNET_JSON_pack_array_steal ("blind_signatures", + blind_signatures)); + } + } + + /* calculate the sum of all receipts */ + + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (DH_currency, + &receipts_sum)); + for (size_t i = 0; i < num_bkps; i++) + { + struct DH_DonationUnitKey *dk; + + if (NULL == (dk = DH_keys_donation_unit_by_hash ( + &bkps[i].h_donation_unit_pub))) + { + GNUNET_break_op (0); + free_bkps (num_bkps, + bkps); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_DONAU_GENERIC_KEYS_MISSING, + NULL); + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "public key value: %lu.%u\n", + dk->value.value, + dk->value.fraction); + GNUNET_assert (0 <= TALER_amount_add (&receipts_sum, + &receipts_sum, + &dk->value)); + } + + /* sign budis and send the signatures back */ + { + struct DONAU_BlindedDonationUnitSignature du_sigs[num_bkps]; + struct DONAU_BkpSignData bkps_sign_data[num_bkps]; + enum TALER_ErrorCode batch_sign_ec; + enum GNUNET_DB_QueryStatus qs_insert_ir; + bool smaller_than_max_per_year = false; + + for (size_t i = 0; i < num_bkps; i++) + { + bkps_sign_data[i].h_donation_unit_pub = &bkps[i].h_donation_unit_pub; + bkps_sign_data[i].budi = &bkps[i].blinded_udi; + } + batch_sign_ec = DH_keys_donation_unit_batch_sign (num_bkps, + bkps_sign_data, + du_sigs); + if (TALER_EC_NONE != batch_sign_ec) + { + GNUNET_break_op (0); + free_bkps (num_bkps, + bkps); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + batch_sign_ec, + NULL); + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "made blind signatures!\n"); + free_bkps (num_bkps, + bkps); + + /* save new receipts to date and save receipts Request (blinded signatures, + * charity id, amount, hash over bkps) to make it idempotent*/ + qs_insert_ir = DH_plugin->insert_issued_receipt ( + DH_plugin->cls, + num_bkps, + du_sigs, + (uint64_t) charity_id, + &h_receipts, + &receipts_sum, + &smaller_than_max_per_year); + switch (qs_insert_ir) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_assert (! second_time); + second_time = true; + goto start; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + if (! smaller_than_max_per_year) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_DONAU_EXCEEDING_DONATION_LIMIT, + NULL); + } + break; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "issue receipts request is saved! (idempotent)\n"); + + signatures_to_json (num_bkps, + du_sigs, + blind_signatures); + for (unsigned int i = 0; i<num_bkps; i++) + GNUNET_CRYPTO_blinded_sig_decref (du_sigs[i].blinded_sig); + } + return TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_OK, + TALER_JSON_pack_amount ("issued_amount", + &receipts_sum), + GNUNET_JSON_pack_array_steal ("blind_signatures", + blind_signatures)); +} + + +/* end of donau-httpd_post-batch-issue-CHARITY_ID.c */ diff --git a/src/donau/donau-httpd_post-batch-issue-CHARITY_ID.h b/src/donau/donau-httpd_post-batch-issue-CHARITY_ID.h @@ -0,0 +1,41 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_post-batch-issue-CHARITY_ID.h + * @brief Handle /batch-issue requests + * @author Lukas Matyja + */ +#ifndef DONAU_HTTPD_POST_BATCH_ISSUE_CHARITY_ID_H +#define DONAU_HTTPD_POST_BATCH_ISSUE_CHARITY_ID_H + +#include <microhttpd.h> +#include "donau-httpd.h" + + +/** + * Handle a POST "/batch-issue" request. + * + * @param connection the MHD connection to handle + * @param root uploaded JSON data + * @return MHD result code + */ +MHD_RESULT +DH_handler_post_batch_issue ( + struct DH_RequestContext *rc, + const json_t *root, + const char *const args[1]); + +#endif diff --git a/src/donau/donau-httpd_post-batch-submit.c b/src/donau/donau-httpd_post-batch-submit.c @@ -0,0 +1,255 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_post-batch-submit.c + * @brief Handle request to insert a submitted receipt. + * @author Johannes Casaburi + */ +#include "donau_config.h" +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_json_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include <pthread.h> +#include "taler/taler_json_lib.h" +#include "taler/taler_mhd_lib.h" +#include "taler/taler_signatures.h" +#include "donaudb_plugin.h" +#include "donau-httpd_post-batch-submit.h" +#include "donau-httpd_get-keys.h" + + +/** + * Closure for #insert_submitted_receipts() + */ +struct InsertReceiptContext +{ + struct DONAU_HashDonorTaxId h_donor_tax_id; + struct DONAU_DonationReceipt *donation_receipts; + size_t num_dr; + uint64_t donation_year; +}; + +/** + * Parse a donation receipt encoded in JSON. + * + * @param[out] dr where to return the result + * @param dr_obj json to parse + * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if @a dr_obj + * is malformed. + */ +static enum GNUNET_GenericReturnValue +parse_json_dr (struct DONAU_DonationReceipt *dr, + const json_t *dr_obj) +{ + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("h_donation_unit_pub", + &dr->h_donation_unit_pub), + GNUNET_JSON_spec_fixed_auto ("nonce", + &dr->nonce), + GNUNET_JSON_spec_unblinded_signature ("donation_unit_sig", + &dr->donation_unit_sig.unblinded_sig), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (dr_obj, + spec, + NULL, + NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + return GNUNET_OK; +} + + +/** + * Free data in @a irc, but not @a irc itself + * + * @param[in,out] irc data structure to clean up + */ +static void +free_irc (struct InsertReceiptContext *irc) +{ + for (size_t i = 0; i<irc->num_dr; i++) + GNUNET_CRYPTO_unblinded_sig_decref (irc->donation_receipts[i]. + donation_unit_sig.unblinded_sig); + GNUNET_free (irc->donation_receipts); +} + + +MHD_RESULT +DH_handler_post_batch_submit (struct DH_RequestContext *rc, + const json_t *root, + const char *const args[]) +{ + struct InsertReceiptContext irc = {0}; + const json_t *donation_receipts; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("h_donor_tax_id", + &irc.h_donor_tax_id), + GNUNET_JSON_spec_array_const ("donation_receipts", + &donation_receipts), + GNUNET_JSON_spec_uint64 ("donation_year", + &irc.donation_year), + GNUNET_JSON_spec_end () + }; + size_t num_dr; + + (void) args; + { + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (rc->connection, + root, + spec); + if (GNUNET_SYSERR == res) + return MHD_NO; /* hard failure */ + if (GNUNET_NO == res) + { + GNUNET_break_op (0); + return MHD_YES; /* failure */ + } + } + + /* parse the donation receipts */ + num_dr = json_array_size (donation_receipts); + + if (0 == num_dr) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "donation_receipts"); + } + { + json_t *dr_obj; + size_t index; + + irc.num_dr = num_dr; + irc.donation_receipts = GNUNET_new_array (num_dr, + struct DONAU_DonationReceipt); + + json_array_foreach (donation_receipts, + index, + dr_obj) + { + if (GNUNET_SYSERR == + parse_json_dr (&irc.donation_receipts[index], + dr_obj)) + { + GNUNET_break_op (0); + free_irc (&irc); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "donation_receipts"); + } + } + } + + for (size_t i = 0; i < num_dr; i++) + { + struct DONAU_UniqueDonorIdentifierHashP udi_hash; + struct DH_DonationUnitKey *dk; + + /* Check nonce unique*/ + for (size_t j = i + 1; j < num_dr; j++) + { + if (0 == + GNUNET_memcmp (&irc.donation_receipts[i].nonce, + &irc.donation_receipts[j].nonce)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Donation receipt nonce is not unique!\n"); + free_irc (&irc); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_CONFLICT, + TALER_EC_DONAU_DONOR_IDENTIFIER_NONCE_REUSE, + NULL); + } + } + + /* Check if donation unit exists*/ + if (NULL == (dk = DH_keys_donation_unit_by_hash ( + &irc.donation_receipts[i].h_donation_unit_pub))) + { + GNUNET_break_op (0); + free_irc (&irc); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_DONAU_GENERIC_DONATION_UNIT_UNKNOWN, + NULL); + } + + DONAU_unique_donor_id_hash ( + &irc.h_donor_tax_id, + &irc.donation_receipts[i].nonce, + &udi_hash); + + /* Check signature*/ + if (GNUNET_OK != DONAU_donation_receipt_verify ( + &dk->donation_unit_pub, + &udi_hash, + &irc.donation_receipts[i].donation_unit_sig)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Donation receipt signature invalid!\n"); + free_irc (&irc); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_DONAU_DONATION_RECEIPT_SIGNATURE_INVALID, + NULL); + + } + } + + { + enum GNUNET_DB_QueryStatus qs; + + qs = DH_plugin->insert_submitted_receipts ( + DH_plugin->cls, + &irc.h_donor_tax_id, + num_dr, + irc.donation_receipts, + irc.donation_year); + free_irc (&irc); + if (qs < 0) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + } + } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "submitted receipts inserted!\n"); + + return TALER_MHD_reply_static ( + rc->connection, + MHD_HTTP_CREATED, + NULL, + NULL, + 0); +} + + +/* end of donau-httpd_post-batch-submit.c */ diff --git a/src/donau/donau-httpd_post-batch-submit.h b/src/donau/donau-httpd_post-batch-submit.h @@ -0,0 +1,41 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_post-batch-submit.h + * @brief Handle /submit requests + * @author Johannes Casaburi + */ +#ifndef DONAU_HTTPD_POST_BATCH_SUBMIT_H +#define DONAU_HTTPD_POST_BATCH_SUBMIT_H + +#include <microhttpd.h> +#include "donau-httpd.h" + + +/** + * Handle a POST "/submit" request. + * + * @param connection the MHD connection to handle + * @param root uploaded JSON data + * @return MHD result code + */ +MHD_RESULT +DH_handler_post_batch_submit ( + struct DH_RequestContext *rc, + const json_t *root, + const char *const args[]); + +#endif diff --git a/src/donau/donau-httpd_post-charities.c b/src/donau/donau-httpd_post-charities.c @@ -0,0 +1,113 @@ +/* + This file is part of TALER + Copyright (C) 2024, 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_post-charities.c + * @brief Handle request to insert a charity. + * @author Johannes Casaburi + * @author Christian Grothoff + */ +#include <donau_config.h> +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_json_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include <pthread.h> +#include <taler/taler_json_lib.h> +#include <taler/taler_mhd_lib.h> +#include <taler/taler_signatures.h> +#include "donaudb_plugin.h" +#include "donau-httpd_post-charities.h" +#include "donau-httpd_db.h" + + +MHD_RESULT +DH_handler_post_charities (struct DH_RequestContext *rc, + const json_t *root, + const char *const args[]) +{ + struct DONAU_CharityPublicKeyP charity_pub; + const char *charity_name; + const char *charity_url; + struct TALER_Amount max_per_year; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("charity_pub", + &charity_pub), + GNUNET_JSON_spec_string ("charity_name", + &charity_name), + GNUNET_JSON_spec_string ("charity_url", + &charity_url), + TALER_JSON_spec_amount ("max_per_year", + DH_currency, + &max_per_year), + GNUNET_JSON_spec_end () + }; + enum GNUNET_DB_QueryStatus qs; + uint64_t charity_id; + + (void) args; + { + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (rc->connection, + root, + spec); + if (GNUNET_SYSERR == res) + return MHD_NO; /* hard failure */ + if (GNUNET_NO == res) + { + GNUNET_break_op (0); + return MHD_YES; /* failure */ + } + } + qs = DH_plugin->insert_charity (DH_plugin->cls, + &charity_pub, + charity_name, + charity_url, + &max_per_year, + &charity_id); + switch (qs) + { + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_charity"); + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_charity"); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_CONFLICT, + TALER_EC_DONAU_CHARITY_PUB_EXISTS, + NULL); + return GNUNET_DB_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + return TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_CREATED, + GNUNET_JSON_pack_uint64 ("charity_id", + charity_id)); +} + + +/* end of donau-httpd_post-charities.c */ diff --git a/src/donau/donau-httpd_post-charities.h b/src/donau/donau-httpd_post-charities.h @@ -0,0 +1,42 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_post-charities.h + * @brief Handle POST /charities request + * @author Johannes Casaburi + */ +#ifndef DONAU_HTTPD_POST_CHARITIES_H +#define DONAU_HTTPD_POST_CHARITIES_H + +#include <microhttpd.h> +#include "donau-httpd.h" + + +/** + * Handle a POST "/charities" request. + * + * @param rc request context + * @param root uploaded JSON data + * @param args arguments (empty) + * @return MHD result code + */ +MHD_RESULT +DH_handler_post_charities ( + struct DH_RequestContext *rc, + const json_t *root, + const char *const args[]); + +#endif diff --git a/src/donau/donau-httpd_post-csr-issue.c b/src/donau/donau-httpd_post-csr-issue.c @@ -0,0 +1,126 @@ +/* + This file is part of TALER + 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 Foundation; either version 3, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General + Public License along with TALER; see the file COPYING. If not, + see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_post-csr-issue.c + * @brief Handle /csr requests + * @author Johannes Casaburi + */ +#include <donau_config.h> +#include <gnunet/gnunet_util_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include <pthread.h> +#include <taler/taler_json_lib.h> +#include <taler/taler_mhd_lib.h> +#include <taler/taler_signatures.h> +#include "donaudb_plugin.h" +#include "donau-httpd_get-keys.h" +#include "donau-httpd_post-csr-issue.h" + + +/** + * Maximum number of csr records we return per request. + */ +#define MAX_RECORDS 1024 + + +MHD_RESULT +DH_handler_post_csr_issue (struct DH_RequestContext *rc, + const json_t *root, + const char *const args[]) +{ + struct GNUNET_CRYPTO_CsSessionNonce nonce; + struct DONAU_DonationUnitHashP du_pub_hash; + struct GNUNET_CRYPTO_BlindingInputValues ewv = { + .cipher = GNUNET_CRYPTO_BSA_CS + }; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("nonce", + &nonce), + GNUNET_JSON_spec_fixed_auto ("du_pub_hash", + &du_pub_hash), + GNUNET_JSON_spec_end () + }; + struct DH_DonationUnitKey *dk; + + (void) args; + { + 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; + } + + { + dk = DH_keys_donation_unit_by_hash (&du_pub_hash); + if (NULL == dk) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_DONAU_GENERIC_KEYS_MISSING, + NULL); + } + if (GNUNET_CRYPTO_BSA_CS != + dk->donation_unit_pub.bsign_pub_key->cipher) + { + /* donation_unit is valid but not for CS */ + GNUNET_break (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_DONAU_GENERIC_KEYS_MISSING, + NULL); + } + } + + /* derive r_pub */ + { + enum TALER_ErrorCode ec; + + ec = DH_keys_donation_unit_cs_r_pub (&du_pub_hash, + &nonce, + &ewv.details.cs_values); + if (TALER_EC_NONE != ec) + { + GNUNET_break (0); + return TALER_MHD_reply_with_ec (rc->connection, + ec, + NULL); + } + } + { + struct TALER_ExchangeBlindingValues exw = { + .blinding_inputs = &ewv + }; + + return TALER_MHD_REPLY_JSON_PACK ( + rc->connection, + MHD_HTTP_CREATED, + TALER_JSON_pack_exchange_blinding_values ("ewv", + &exw)); + } +} + + +/* end of donau-httpd_post-csr-issue.c */ diff --git a/src/donau/donau-httpd_post-csr-issue.h b/src/donau/donau-httpd_post-csr-issue.h @@ -0,0 +1,42 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file donau-httpd_post-csr-issue.h + * @brief Handle /csr-* requests + * @author Johannes Casaburi + */ +#ifndef DONAU_HTTPD_POST_CSR_ISSUE_H +#define DONAU_HTTPD_POST_CSR_ISSUE_H + +#include <microhttpd.h> +#include "donau-httpd.h" +#include "donaudb_plugin.h" + + +/** + * Handle a "/csr-issue" request. + * + * @param rc request context + * @param root uploaded JSON data + * @param args empty array + * @return MHD result code + */ +MHD_RESULT +DH_handler_post_csr_issue (struct DH_RequestContext *rc, + const json_t *root, + const char *const args[]); + +#endif