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:
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