donau

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

commit 30c3076f316de8139bb129ccad340aa3a0d9ba28
parent f4d80b5ddc929aeacb428d66741482e0dbcb1a89
Author: Casaburi Johannes <johannes.casaburi@students.bfh.ch>
Date:   Wed, 24 Apr 2024 12:25:40 +0200

Merge remote-tracking branch 'refs/remotes/origin/master'

Diffstat:
Msrc/donau/donau-httpd_keys.c | 272++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/include/donau_service.h | 8++++++--
Msrc/include/donaudb_plugin.h | 10+++++-----
Msrc/lib/Makefile.am | 7++++---
Asrc/lib/donau_api_batch_submit_receipts.c | 253+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 404 insertions(+), 146 deletions(-)

diff --git a/src/donau/donau-httpd_keys.c b/src/donau/donau-httpd_keys.c @@ -1293,142 +1293,142 @@ DH_keys_donation_unit_batch_sign ( 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) - // // FIXME change error code - // return TALER_EC_EXCHANGE_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) - // // FIXME change error code - // return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_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; - // } - // // TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_CS] += csrs_pos; - // } - // 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; - // } - // // TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_RSA] += rsrs_pos; - // } - - // 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); - // } - // } + 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) + // FIXME change error code + return TALER_EC_EXCHANGE_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) + // FIXME change error code + return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_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; + } + // TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_CS] += csrs_pos; + } + 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; + } + // TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_RSA] += rsrs_pos; + } + + 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; } diff --git a/src/include/donau_service.h b/src/include/donau_service.h @@ -643,6 +643,8 @@ typedef void * @param url donau base URL * @param num_drs length of the @a drs array * @param drs array with details about the donation receipts + * @param year corresponding year + * @param h_tax_id salted and hashed tax id * @param cb the callback to call when a reply for this request is available * @param cls closure for the above callback * @param[out] ec if NULL is returned, set to the error code explaining why the operation failed @@ -653,8 +655,10 @@ struct DONAU_DonorReceiptsToStatementHandle * DONAU_donor_receipts_to_statement ( struct GNUNET_CURL_Context *ctx, const char *url, - unsigned int num_drs, - const struct DONAU_DonationReceipt drs[static num_drs], + const size_t num_drs, + const struct DONAU_DonationReceipt drs[num_drs], + const uint64_t year, + const struct DONAU_HashDonorTaxId *h_tax_id, DONAU_DonorReceiptsToStatementResultCallback cb, void *cls); diff --git a/src/include/donaudb_plugin.h b/src/include/donaudb_plugin.h @@ -528,11 +528,11 @@ struct DONAUDB_Plugin */ enum GNUNET_DB_QueryStatus (*insert_submitted_receipts)( - void *cls, - const struct DONAU_HashDonorTaxId *h_tax_number, - const struct DONAU_DonationReceipt *donation_receipts, - const size_t num_dr, - const uint64_t donation_year); + void *cls, + const struct DONAU_HashDonorTaxId *h_tax_number, + const struct DONAU_DonationReceipt *donation_receipts, + const size_t num_dr, + const uint64_t donation_year); /** * Lookup issued receipts from the charity. diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am @@ -25,9 +25,10 @@ libdonau_la_SOURCES = \ donau_api_charity_post.c \ donau_api_charity_delete.c \ donau_api_charities_get.c \ - donau_api_curl_defaults.c donau_api_curl_defaults.h \ - donau_api_batch_issue_receipts.c - + donau_api_curl_defaults.c donau_api_curl_defaults.h \ + donau_api_batch_issue_receipts.c \ + # donau_api_batch_submit_receipts.c + ## maybe need libtalercurl libdonau_la_LIBADD = \ $(top_builddir)/src/util/libdonauutil.la \ diff --git a/src/lib/donau_api_batch_submit_receipts.c b/src/lib/donau_api_batch_submit_receipts.c @@ -0,0 +1,253 @@ +/* + 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 General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + TALER is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, see + <http://www.gnu.org/licenses/> +*/ + +/** + * @file lib/donau_api_batch_submit_receipts.c + * @brief Implementation of the "handle" component of the donau's HTTP API + * @author Lukas Matyja + */ +#include <gnunet/gnunet_curl_lib.h> +#include <taler/taler_json_lib.h> +#include <taler/taler_curl_lib.h> +#include "donau_service.h" +#include "donau_util.h" +#include "donau_api_curl_defaults.h" +#include "donau_json_lib.h" + + +/** + * Handle for a POST /submit request. + */ +struct DONAU_DonorReceiptsToStatementHandle +{ + /** + * The url for the /submit request. + */ + char *url; + + /** + * Minor context that holds body and headers. + */ + struct TALER_CURL_PostContext post_ctx; + + /** + * Entry for this request with the `struct GNUNET_CURL_Context`. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + DONAU_DonorReceiptsToStatementResultCallback cb; + + /** + * BUDI-key-pair signature. + */ + struct DONAU_CharitySignatureP charity_sig; + + /** + * Closure to pass to @e cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + +/** + * Transform submit receipt request into JSON. + * + * @param num_drs number of donation receipts in @drs + * @param drs donation receipts array + * @param year corresponding year + * @param h_tax_id salted and hashed tax id + */ +json_t * +submit_request_body_to_json (const size_t num_drs, + const struct + DONAU_DonationReceipt drs[num_drs], + const uint64_t year, + const struct DONAU_HashDonorTaxId *h_tax_id) +{ + json_t *donation_receipts = json_array (); + GNUNET_assert (NULL != donation_receipts); + + for (size_t i = 0; i < num_drs; i++) + { + json_t *receipt = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("h_donation_unit_pub", + &drs[i]->h_donation_unit_pub), + GNUNET_JSON_pack_data_auto ("nonce", + &drs[i]->nonce), + GNUNET_JSON_pack_data_auto ("donau_sig", + &drs[i]->donau_sig)); + GNUNET_assert (0 == + json_array_append_new (donation_receipts, + receipt)); + } + return GNUNET_JSON_PACK ( + GNUNET_JSON_pack_array_steal ("donation_receipts", + donation_receipts), + GNUNET_JSON_pack_data_auto ("h_tax_number", + h_tax_id), + GNUNET_JSON_pack_uint64 ("donation_year", + year)); +} + + +/** + * Function called when we're done processing the + * HTTP POST /submit request. + * + * @param cls the `struct KeysRequest` + * @param response_code HTTP response code, 0 on error + * @param resp_obj parsed JSON result, NULL on error + */ +static void +handle_batch_submit_finished (void *cls, + long response_code, + const void *resp_obj) +{ + struct DONAU_DonorReceiptsToStatementHandle *birh = cls; + const json_t *j = resp_obj; + + struct DONAU_DonorReceiptsToStatementResult biresp = { + .hr.reply = j, + .hr.http_status = (unsigned int) response_code + }; + + birh->job = NULL; + switch (response_code) + { + case MHD_HTTP_CREATED: + break; + case MHD_HTTP_NO_CONTENT: + biresp.hr.ec = TALER_JSON_get_error_code (j); + biresp.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_FORBIDDEN: + biresp.hr.ec = TALER_JSON_get_error_code (j); + biresp.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_NOT_FOUND: + biresp.hr.ec = TALER_JSON_get_error_code (j); + biresp.hr.hint = TALER_JSON_get_error_hint (j); + break; + case MHD_HTTP_CONTENT_TOO_LARGE: + biresp.hr.ec = TALER_JSON_get_error_code (j); + biresp.hr.hint = TALER_JSON_get_error_hint (j); + break; + default: + /* unexpected response code */ + GNUNET_break_op (0); + biresp.hr.ec = TALER_JSON_get_error_code (j); + biresp.hr.hint = TALER_JSON_get_error_hint (j); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u/%d for POST %s\n", + (unsigned int) response_code, + (int) biresp.hr.ec, + birh->url); + break; + } + if (NULL != birh->cb) + { + birh->cb (birh->cb_cls, + &biresp); + birh->cb = NULL; + } + DONAU_donor_receipts_to_statement_cancel (birh); +} + + +struct DONAU_DonorReceiptsToStatementHandle * +DONAU_donor_receipts_to_statement ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const size_t num_drs, + const struct DONAU_DonationReceipt drs[num_drs], + const uint64_t year, + const struct DONAU_HashDonorTaxId *h_tax_id, + DONAU_DonorReceiptsToStatementResultCallback cb, + void *cls) +{ + struct DONAU_DonorReceiptsToStatementHandle *birh; + birh = GNUNET_new (struct DONAU_DonorReceiptsToStatementHandle); + CURL *eh; + json_t *body; + + TALER_LOG_DEBUG ("Connecting to the donau (%s)\n", + url); + birh->url = GNUNET_strdup (url); + birh->cb = cb; + birh->cb_cls = cls; + birh->ctx = ctx; + birh->url = TALER_url_join (url, + "submit", + NULL); + if (NULL == birh->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + GNUNET_free (birh); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "submit_receipts_with_URL `%s'.\n", + birh->url); + body = submit_request_body_to_json (num_drs, drs, year, h_tax_id); + eh = DONAU_curl_easy_get_ (birh->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&birh->post_ctx, + eh, + body)) ) + { + GNUNET_break (0); + if (NULL != eh) + curl_easy_cleanup (eh); + json_decref (body); + GNUNET_free (birh->url); + return NULL; + } + json_decref (body); + birh->job = GNUNET_CURL_job_add2 (ctx, + eh, + birh->post_ctx.headers, + &handle_batch_submit_finished, + birh); + return birh; +} + + +void +DONAU_donor_receipts_to_statement_cancel ( + struct DONAU_DonorReceiptsToStatementHandle *drsh) +{ + if (NULL != drsh->job) + { + GNUNET_CURL_job_cancel (drsh->job); + drsh->job = NULL; + } + TALER_curl_easy_post_finished (&drsh->post_ctx); + GNUNET_free (drsh->url); + GNUNET_free (drsh); +}