donau

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

commit dffa7bd9e4d1938ffc2e2c3fb5a4714aad5fb648
parent e0db82e03074aa3db0e75f66c64c66b224dedf96
Author: Christian Grothoff <christian@grothoff.org>
Date:   Fri, 26 Sep 2025 15:36:10 +0200

fix #10456

Diffstat:
Mcontrib/gana-generate.sh | 2+-
Msrc/donau/donau-httpd.c | 46++++++++++++++++++++++++++++++++++++----------
Msrc/donau/donau-httpd_charity.h | 7+++++--
Msrc/donau/donau-httpd_charity_get.c | 20++++++++++++++++----
Msrc/include/donau_crypto_lib.h | 26++++++++++++++++++++++++++
Msrc/include/donau_service.h | 6+++---
Msrc/include/donau_signatures.h | 6++++++
Msrc/include/donau_testing_lib.h | 27+++++++++++++--------------
Msrc/include/donau_util.h | 5+++++
Msrc/lib/donau_api_charity_get.c | 25+++++++++++++++++--------
Msrc/testing/test_donau_api.c | 1-
Msrc/testing/testing_api_cmd_charity_get.c | 25++++++++++++++-----------
Msrc/util/charity_signatures.c | 34++++++++++++++++++++++++++++++++++
13 files changed, 176 insertions(+), 54 deletions(-)

diff --git a/contrib/gana-generate.sh b/contrib/gana-generate.sh @@ -13,7 +13,7 @@ if [ ! -f $SRC_ROOT/contrib/gana-generate.sh ]; then fi COMMIT_HASH="" -if [ ! -z $1 ]; then +if [ ! -z "${1:-}" ]; then COMMIT_HASH=$1 fi diff --git a/src/donau/donau-httpd.c b/src/donau/donau-httpd.c @@ -408,22 +408,43 @@ proceed_with_handler (struct DH_RequestContext *rc, /** - * Handle a "/charities/$CHARITY_ID" GET request. + * Handle a "/charities" GET request. * * @param rc request context - * @param optional charity id (args[0]) + * @param args empty array * @return MHD result code */ static MHD_RESULT handle_get_charities (struct DH_RequestContext *rc, - const char *const args[1]) + const char *const args[]) { - if (NULL == args[0]) - { - return DH_handler_charities_get (rc); - } + GNUNET_break (NULL == args[0]); + return DH_handler_charities_get (rc); +} + + +/** + * Handle a "/charities/$CHARITY_ID" GET request. + * + * @param rc request context + * @param args charity id (in args[0]) + * @return MHD result code + */ +static MHD_RESULT +handle_get_charity_id (struct DH_RequestContext *rc, + const char *const args[1]) +{ + struct DONAU_CharitySignatureP charity_sig; + bool sig_required = true; + + TALER_MHD_parse_request_header_auto ( + rc->connection, + DONAU_HTTP_HEADER_CHARITY_SIGNATURE, + &charity_sig, + sig_required); return DH_handler_charity_get (rc, - &args[0]); + &charity_sig, + args[0]); } @@ -480,10 +501,15 @@ handle_mhd_request (void *cls, .url = "charities", .method = MHD_HTTP_METHOD_GET, .handler.get = &handle_get_charities, - .nargs = 1, - .nargs_is_upper_bound = true, .needs_authorization = true }, + /* GET charity/$ID */ + { + .url = "charity", + .method = MHD_HTTP_METHOD_GET, + .handler.get = &handle_get_charity_id, + .nargs = 1, + }, /* POST charities */ { .url = "charities", diff --git a/src/donau/donau-httpd_charity.h b/src/donau/donau-httpd_charity.h @@ -44,13 +44,16 @@ DH_handler_charity_post ( * Handle a GET "/charities/$CHARITY_ID" request. * * @param rc request context - * @param args GET arguments (should be one) + * @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 char *const args[1]); + const struct DONAU_CharitySignatureP *charity_sig, + const char *charity_id_s); /** diff --git a/src/donau/donau-httpd_charity_get.c b/src/donau/donau-httpd_charity_get.c @@ -35,16 +35,18 @@ */ #define MAX_RECORDS 1024 + MHD_RESULT DH_handler_charity_get ( struct DH_RequestContext *rc, - const char *const args[1]) + const struct DONAU_CharitySignatureP *charity_sig, + const char *charity_id_s) { unsigned long long charity_id; char dummy; - if ( (NULL == args[0]) || - (1 != sscanf (args[0], + if ( (NULL == charity_id_s) || + (1 != sscanf (charity_id_s, "%llu%c", &charity_id, &dummy)) ) @@ -78,12 +80,22 @@ DH_handler_charity_get ( rc->connection, MHD_HTTP_NOT_FOUND, TALER_EC_DONAU_CHARITY_NOT_FOUND, - args[0]); + 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, diff --git a/src/include/donau_crypto_lib.h b/src/include/donau_crypto_lib.h @@ -441,6 +441,32 @@ DONAU_charity_bkp_verify ( const struct DONAU_CharitySignatureP *charity_sig); +/** + * Create charity eddsa signature approving a request for charity + * status information. + * + * @param charity_priv private key of the charity + * @param[out] charity_sig where to write the signature + */ +void +DONAU_charity_get_info_sign ( + const struct DONAU_CharityPrivateKeyP *charity_priv, + struct DONAU_CharitySignatureP *charity_sig); + + +/** + * Verify charity eddsa signature requesting charity information. + * + * @param charity_pub public key of the charity + * @param charity_sig where to write the signature + * @return #GNUNET_OK if the signature is valid + */ +enum GNUNET_GenericReturnValue +DONAU_charity_get_info_verify ( + const struct DONAU_CharityPublicKeyP *charity_pub, + const struct DONAU_CharitySignatureP *charity_sig); + + /* ********************* donau eddsa signing ************************** */ /** diff --git a/src/include/donau_service.h b/src/include/donau_service.h @@ -1151,8 +1151,8 @@ typedef void * * @param ctx curl context * @param url donau base URL - * @param bearer for authorization * @param id of the requested charity + * @param charity_priv private key of the charity, for authorization * @param cb the callback to call when a reply for this request is available * @param cb_cls closure for the above callback * @return a handle for this request; NULL if the inputs are invalid (i.e. @@ -1162,8 +1162,8 @@ struct DONAU_CharityGetHandle * DONAU_charity_get ( struct GNUNET_CURL_Context *ctx, const char *url, - const uint64_t id, - const struct DONAU_BearerToken *bearer, + uint64_t id, + const struct DONAU_CharityPrivateKeyP *charity_priv, DONAU_GetCharityResponseCallback cb, void *cb_cls); diff --git a/src/include/donau_signatures.h b/src/include/donau_signatures.h @@ -38,4 +38,10 @@ #define DONAU_SIGNATURE_CHARITY_DONATION_CONFIRMATION 1501 +/** + * The signature is made by a charity to request information about its status from a Donau. It is not over anything in particular and is just there for access control. + */ +#define DONAU_SIGNATURE_CHARITY_GET_INFO 1502 + + #endif diff --git a/src/include/donau_testing_lib.h b/src/include/donau_testing_lib.h @@ -47,19 +47,18 @@ TALER_TESTING_cmd_get_donau ( const struct GNUNET_CONFIGURATION_Handle *cfg, bool wait_for_keys); + /** * Create a GET "charity" command. * * @param label the command label. * @param charity_reference reference for traits - * @param bearer authorization token * @param expected_response_code expected HTTP response code. * @return the command. */ struct TALER_TESTING_Command TALER_TESTING_cmd_charity_get (const char *label, const char *charity_reference, - const struct DONAU_BearerToken *bearer, unsigned int expected_response_code); /** @@ -203,24 +202,24 @@ TALER_TESTING_get_donau_url ( * Call #op on all simple traits. */ #define DONAU_TESTING_SIMPLE_TRAITS(op) \ - op (charity_priv, const struct DONAU_CharityPrivateKeyP) \ - op (charity_pub, const struct DONAU_CharityPublicKeyP) \ - op (charity_id, const uint64_t) \ - op (donau_url, const char) \ - op (donau_keys, struct DONAU_Keys) \ - op (donor_salt, const char) \ - op (donor_tax_id, const char) \ - op (salted_tax_id_hash, const struct DONAU_HashDonorTaxId) \ - op (donation_receipts, const struct DONAU_DonationReceipt*) \ - op (number_receipts, const size_t) + op (charity_priv, const struct DONAU_CharityPrivateKeyP) \ + op (charity_pub, const struct DONAU_CharityPublicKeyP) \ + op (charity_id, const uint64_t) \ + op (donau_url, const char) \ + op (donau_keys, struct DONAU_Keys) \ + op (donor_salt, const char) \ + op (donor_tax_id, const char) \ + op (salted_tax_id_hash, const struct DONAU_HashDonorTaxId) \ + op (donation_receipts, const struct DONAU_DonationReceipt*) \ + op (number_receipts, const size_t) /** * Call #op on all indexed traits. */ #define DONAU_TESTING_INDEXED_TRAITS(op) \ - op (donation_unit_pub, const struct DONAU_DonationUnitInformation) \ - op (donau_pub, const struct TALER_ExchangePublicKeyP) + op (donation_unit_pub, const struct DONAU_DonationUnitInformation) \ + op (donau_pub, const struct TALER_ExchangePublicKeyP) DONAU_TESTING_SIMPLE_TRAITS (TALER_TESTING_MAKE_DECL_SIMPLE_TRAIT) DONAU_TESTING_INDEXED_TRAITS (TALER_TESTING_MAKE_DECL_INDEXED_TRAIT) diff --git a/src/include/donau_util.h b/src/include/donau_util.h @@ -30,6 +30,11 @@ #include "donau_crypto_lib.h" /** + * HTTP header with a signature by the charity. + */ +#define DONAU_HTTP_HEADER_CHARITY_SIGNATURE "Charity-Signature" + +/** * Return default project data used by Taler. */ const struct GNUNET_OS_ProjectData * diff --git a/src/lib/donau_api_charity_get.c b/src/lib/donau_api_charity_get.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2024 Taler Systems SA + 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 General Public License as published @@ -194,7 +194,7 @@ DONAU_charity_get ( struct GNUNET_CURL_Context *ctx, const char *url, const uint64_t id, - const struct DONAU_BearerToken *bearer, // for authorization + const struct DONAU_CharityPrivateKeyP *charity_priv, DONAU_GetCharityResponseCallback cb, void *cb_cls) { @@ -211,7 +211,7 @@ DONAU_charity_get ( cgh->cb_cls = cb_cls; GNUNET_snprintf (arg_str, sizeof (arg_str), - "charities/%llu", + "charity/%llu", (unsigned long long) id); cgh->url = TALER_url_join (url, @@ -238,15 +238,23 @@ DONAU_charity_get ( &handle_charity_get_finished, cgh); GNUNET_assert (NULL != cgh->job); - if (NULL != bearer) { - struct curl_slist *auth; + struct DONAU_CharitySignatureP charity_sig; + char *sig_hdr; char *hdr; + struct curl_slist *auth; + DONAU_charity_get_info_sign (charity_priv, + &charity_sig); + + sig_hdr = GNUNET_STRINGS_data_to_string_alloc ( + &charity_sig, + sizeof (charity_sig)); GNUNET_asprintf (&hdr, - "%s: Bearer %s", - MHD_HTTP_HEADER_AUTHORIZATION, - bearer->token); + "%s: %s", + DONAU_HTTP_HEADER_CHARITY_SIGNATURE, + sig_hdr); + GNUNET_free (sig_hdr); auth = curl_slist_append (NULL, hdr); GNUNET_free (hdr); @@ -254,6 +262,7 @@ DONAU_charity_get ( auth); curl_slist_free_all (auth); } + return cgh; } diff --git a/src/testing/test_donau_api.c b/src/testing/test_donau_api.c @@ -81,7 +81,6 @@ run (void *cls, MHD_HTTP_CREATED), TALER_TESTING_cmd_charity_get ("get-charity-by-id", "post-charity", // cmd trait reference - &bearer, MHD_HTTP_OK), TALER_TESTING_cmd_charities_get ("get-charities", &bearer, diff --git a/src/testing/testing_api_cmd_charity_get.c b/src/testing/testing_api_cmd_charity_get.c @@ -40,11 +40,6 @@ struct StatusState struct DONAU_CharityGetHandle *cgh; /** - * The bearer token for authorization. - */ - const struct DONAU_BearerToken *bearer; - - /** * The ID of the requested charity. */ uint64_t charity_id; @@ -110,6 +105,7 @@ status_run (void *cls, struct TALER_TESTING_Interpreter *is) { struct StatusState *ss = cls; + const struct DONAU_CharityPrivateKeyP *charity_priv; (void) cmd; ss->is = is; @@ -118,9 +114,10 @@ status_run (void *cls, const struct TALER_TESTING_Command *charity_post_cmd; const uint64_t *charity_id; - charity_post_cmd = TALER_TESTING_interpreter_lookup_command (is, - ss-> - charity_reference); + charity_post_cmd + = TALER_TESTING_interpreter_lookup_command ( + is, + ss->charity_reference); if (GNUNET_OK != TALER_TESTING_get_trait_charity_id (charity_post_cmd, @@ -130,6 +127,14 @@ status_run (void *cls, TALER_TESTING_interpreter_fail (is); return; } + if (GNUNET_OK != + TALER_TESTING_get_trait_charity_priv (charity_post_cmd, + &charity_priv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } ss->charity_id = *(charity_id); } @@ -137,7 +142,7 @@ status_run (void *cls, TALER_TESTING_interpreter_get_context (is), TALER_TESTING_get_donau_url (is), ss->charity_id, - ss->bearer, + charity_priv, &charity_status_cb, ss); } @@ -171,13 +176,11 @@ status_cleanup (void *cls, struct TALER_TESTING_Command TALER_TESTING_cmd_charity_get (const char *label, const char *charity_reference, - const struct DONAU_BearerToken *bearer, unsigned int expected_response_code) { struct StatusState *ss; ss = GNUNET_new (struct StatusState); ss->expected_response_code = expected_response_code; - ss->bearer = bearer; ss->charity_reference = charity_reference; { struct TALER_TESTING_Command cmd = { diff --git a/src/util/charity_signatures.c b/src/util/charity_signatures.c @@ -157,4 +157,38 @@ DONAU_charity_bkp_verify ( } +enum GNUNET_GenericReturnValue +DONAU_charity_get_info_verify ( + const struct DONAU_CharityPublicKeyP *charity_pub, + const struct DONAU_CharitySignatureP *charity_sig) +{ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose = { + .purpose = htonl (DONAU_SIGNATURE_CHARITY_GET_INFO), + .size = htonl (sizeof (purpose)) + }; + + return GNUNET_CRYPTO_eddsa_verify_ ( + DONAU_SIGNATURE_CHARITY_GET_INFO, + &purpose, + &charity_sig->eddsa_sig, + &charity_pub->eddsa_pub); +} + + +void +DONAU_charity_get_info_sign ( + const struct DONAU_CharityPrivateKeyP *charity_priv, + struct DONAU_CharitySignatureP *charity_sig) +{ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose = { + .purpose = htonl (DONAU_SIGNATURE_CHARITY_GET_INFO), + .size = htonl (sizeof (purpose)) + }; + + GNUNET_CRYPTO_eddsa_sign_ (&charity_priv->eddsa_priv, + &purpose, + &charity_sig->eddsa_sig); +} + + /* end of charity_signatures.c */