commit 17a70bd391732802e13e5639e387048fa869fff2
parent 516adf7e3804025ee58072e78aa733afcf5f99a1
Author: Matyja Lukas Adam <lukas.matyja@students.bfh.ch>
Date: Tue, 14 May 2024 20:20:24 +0200
[testing][lib] add cs client & tests
Diffstat:
9 files changed, 421 insertions(+), 58 deletions(-)
diff --git a/src/donau/donau-httpd.c b/src/donau/donau-httpd.c
@@ -54,7 +54,7 @@
* Above what request latency do we start to log?
*/
#define WARN_LATENCY GNUNET_TIME_relative_multiply ( \
- GNUNET_TIME_UNIT_MILLISECONDS, 500)
+ GNUNET_TIME_UNIT_MILLISECONDS, 500)
/**
* Are clients allowed to request /keys for times other than the
diff --git a/src/donau/donau-httpd_csr.c b/src/donau/donau-httpd_csr.c
@@ -43,8 +43,8 @@
MHD_RESULT
DH_handler_csr_issue (struct DH_RequestContext *rc,
- const json_t *root,
- const char *const args[])
+ const json_t *root,
+ const char *const args[])
{
struct GNUNET_CRYPTO_CsSessionNonce nonce;
struct TALER_DenominationHashP denom_pub_hash;
diff --git a/src/donau/donau-httpd_csr.h b/src/donau/donau-httpd_csr.h
@@ -37,7 +37,7 @@
*/
MHD_RESULT
DH_handler_csr_issue (struct DH_RequestContext *rc,
- const json_t *root,
- const char *const args[]);
+ const json_t *root,
+ const char *const args[]);
#endif
diff --git a/src/include/donau_service.h b/src/include/donau_service.h
@@ -751,13 +751,13 @@ typedef void
* In this case, the callback is not called.
*/
struct DONAU_CsRBatchIssueHandle *
-DONAU_csr_batch_issue (
- struct GNUNET_CURL_Context *curl_ctx,
- const char *donau_url,
+DONAU_csr_issue (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
const struct DONAU_DonationUnitPublicKey *pk,
- const struct GNUNET_CRYPTO_CsBlindingNonce nonce,
- DONAU_CsRBatchIssueCallback res_cb,
- void *res_cb_cls);
+ const struct GNUNET_CRYPTO_CsSessionNonce *nonce,
+ DONAU_CsRBatchIssueCallback cb,
+ void *cb_cls);
/**
@@ -768,7 +768,7 @@ DONAU_csr_batch_issue (
* @param csrh the batch-issue handle
*/
void
-DONAU_csr_batch_issue_cancel (
+DONAU_csr_cancel (
struct DONAU_CsRBatchIssueHandle *csrh);
diff --git a/src/include/donau_testing_lib.h b/src/include/donau_testing_lib.h
@@ -117,6 +117,7 @@ TALER_TESTING_cmd_charities_get (const char *label,
*
* @param label the command label.
* @param charity_reference reference for traits
+ * @param uses_cs true for cs and false for rsa.
* @param year current year (mostly)
* @param donor_tax_id tax id of the donor
* @param salt for tax id hash
@@ -126,6 +127,7 @@ TALER_TESTING_cmd_charities_get (const char *label,
struct TALER_TESTING_Command
TALER_TESTING_cmd_issue_receipts (const char *label,
const char *charity_reference,
+ const bool uses_cs,
const uint64_t year,
const char *donor_tax_id,
const char *salt,
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
@@ -27,7 +27,8 @@ libdonau_la_SOURCES = \
donau_api_charities_get.c \
donau_api_curl_defaults.c donau_api_curl_defaults.h \
donau_api_batch_issue_receipts.c \
- donau_api_batch_submit_receipts.c
+ donau_api_batch_submit_receipts.c \
+ donau_api_csr_post.c
## maybe need libtalercurl
libdonau_la_LIBADD = \
diff --git a/src/lib/donau_api_csr_post.c b/src/lib/donau_api_csr_post.c
@@ -0,0 +1,226 @@
+/*
+ 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_csr_post.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_api_curl_defaults.h"
+#include "donau_json_lib.h"
+
+
+/**
+ * Handle for a POST /csr-issue request.
+ */
+struct DONAU_CsRBatchIssueHandle
+{
+ /**
+ * The url for the /csr-issue 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_CsRBatchIssueCallback cb;
+
+ /**
+ * Closure to pass to @e cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Reference to the execution context.
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+};
+
+/**
+ * Function called when we're done processing the
+ * HTTP POST /csr-issue 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_csr_issue_post_finished (void *cls,
+ long response_code,
+ const void *resp_obj)
+{
+ struct DONAU_CsRBatchIssueHandle *csrh = cls;
+ const json_t *j = resp_obj;
+
+ struct DONAU_CsRBatchIssueResponse csrresp = {
+ .hr.reply = j,
+ .hr.http_status = (unsigned int) response_code
+ };
+
+ csrh->job = NULL;
+ switch (response_code)
+ {
+ case MHD_HTTP_CREATED:
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_exchange_withdraw_values ( // TODO: method for GNUNET
+ "ewv",
+ (struct TALER_ExchangeWithdrawValues *) &csrresp.details.ok.alg_values),
+ GNUNET_JSON_spec_end ()
+ };
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (j,
+ spec,
+ NULL,
+ NULL))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not parse response from csr POST\n");
+ GNUNET_break_op (0);
+ }
+ csrh->cb (csrh->cb_cls,
+ &csrresp);
+ break;
+ // Donation unit was revoked.
+ case MHD_HTTP_GONE:
+ csrresp.hr.ec = TALER_JSON_get_error_code (j);
+ csrresp.hr.hint = TALER_JSON_get_error_hint (j);
+ break;
+ // Donation unit or endpoint not found.
+ case MHD_HTTP_NOT_FOUND:
+ csrresp.hr.ec = TALER_JSON_get_error_code (j);
+ csrresp.hr.hint = TALER_JSON_get_error_hint (j);
+ break;
+ default:
+ /* unexpected response code */
+ GNUNET_break_op (0);
+ csrresp.hr.ec = TALER_JSON_get_error_code (j);
+ csrresp.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) csrresp.hr.ec,
+ csrh->url);
+ break;
+ }
+ if (NULL != csrh->cb)
+ {
+ csrh->cb (csrh->cb_cls,
+ &csrresp);
+ csrh->cb = NULL;
+ }
+ DONAU_csr_cancel (csrh);
+}
+
+
+struct DONAU_CsRBatchIssueHandle *
+DONAU_csr_issue (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const struct DONAU_DonationUnitPublicKey *pk,
+ const struct GNUNET_CRYPTO_CsSessionNonce *nonce,
+ DONAU_CsRBatchIssueCallback cb,
+ void *cb_cls)
+{
+ struct DONAU_CsRBatchIssueHandle *csrh;
+ CURL *eh;
+ json_t *body;
+
+ struct DONAU_DonationUnitHashP h_donation_unit_pub;
+ DONAU_donation_unit_pub_hash (pk,
+ &h_donation_unit_pub);
+
+ TALER_LOG_DEBUG ("Connecting to the donau (%s)\n",
+ url);
+ csrh = GNUNET_new (struct DONAU_CsRBatchIssueHandle);
+ csrh->url = GNUNET_strdup (url);
+ csrh->cb = cb;
+ csrh->cb_cls = cb_cls;
+ csrh->ctx = ctx;
+ csrh->url = TALER_url_join (url,
+ "charities",
+ NULL);
+ if (NULL == csrh->url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Could not construct requested URL.\n");
+ GNUNET_free (csrh);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Request CS R with URL `%s'.\n",
+ csrh->url);
+ body = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_varsize ("nonce",
+ nonce,
+ sizeof(*nonce)),
+ GNUNET_JSON_pack_data_varsize ("donation_unit_pub_hash",
+ &h_donation_unit_pub,
+ sizeof(h_donation_unit_pub)));
+ eh = DONAU_curl_easy_get_ (csrh->url);
+ if ( (NULL == eh) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&csrh->post_ctx,
+ eh,
+ body)) )
+ {
+ GNUNET_break (0);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ json_decref (body);
+ GNUNET_free (csrh->url);
+ return NULL;
+ }
+ json_decref (body);
+ csrh->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ csrh->post_ctx.headers,
+ &handle_csr_issue_post_finished,
+ csrh);
+ return csrh;
+}
+
+
+void
+DONAU_csr_cancel (
+ struct DONAU_CsRBatchIssueHandle *csrh)
+{
+ if (NULL != csrh->job)
+ {
+ GNUNET_CURL_job_cancel (csrh->job);
+ csrh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&csrh->post_ctx);
+ GNUNET_free (csrh->url);
+ GNUNET_free (csrh);
+}
diff --git a/src/testing/test_donau_api.c b/src/testing/test_donau_api.c
@@ -45,11 +45,7 @@ static char *config_file;
static struct TALER_TESTING_Credentials cred;
/**
- * Some tests behave differently when using CS as we cannot
- * reuse the coin private key for different denominations
- * due to the derivation of it with the /csr values. Hence
- * some tests behave differently in CS mode, hence this
- * flag.
+ * Issue receipts tests behave differently when using CS.
*/
static bool uses_cs;
@@ -94,14 +90,15 @@ run (void *cls,
// FIXME
TALER_TESTING_cmd_issue_receipts ("issue-receipts",
"post-charity",
+ uses_cs,
2024,
"7560001010000", // tax id
"1234", // salt for tax id hash
MHD_HTTP_CREATED),
- TALER_TESTING_cmd_submit_receipts ("submit-receipts",
- "issue-receipts", // cmd trait reference
- 2024,
- MHD_HTTP_OK),
+// TALER_TESTING_cmd_submit_receipts ("submit-receipts",
+// "issue-receipts", // cmd trait reference
+// 2024,
+// MHD_HTTP_OK),
TALER_TESTING_cmd_charity_delete ("delete-charity",
"post-charity", // cmd trait reference
&bearer,
diff --git a/src/testing/testing_api_cmd_issue_receipts.c b/src/testing/testing_api_cmd_issue_receipts.c
@@ -34,7 +34,7 @@
struct StatusState
{
/**
- * Handle to the "charity status" operation.
+ * Handle to the "batch issue receipt status" operation.
*/
struct DONAU_BatchIssueReceiptHandle *birh;
@@ -44,16 +44,15 @@ struct StatusState
const char *charity_reference;
/**
- * Private key of the charity, for signature.
- */
- struct DONAU_CharityPrivateKeyP charity_priv;
-
- /**
* Expected HTTP response code.
*/
unsigned int expected_response_code;
/**
+ */
+ bool uses_cs;
+
+ /**
* Interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
@@ -69,6 +68,11 @@ struct StatusState
unsigned long long year;
/**
+ * Private key of the charity, for signature.
+ */
+ struct DONAU_CharityPrivateKeyP charity_priv;
+
+ /**
* number of budi key pair.
*/
size_t num_bkp;
@@ -107,6 +111,35 @@ struct StatusState
* Array of hashed udis.
*/
struct DONAU_UniqueDonorIdentifierHashP *h_udis;
+
+ /**
+ * Number of pending CS requests.
+ */
+ size_t cs_pending;
+};
+
+
+struct CSR_Data
+{
+ /**
+ * Handle to the "batch issue receipt status" operation.
+ */
+ struct DONAU_CsRBatchIssueHandle *csr_handle;
+
+ /**
+ * CS-Nonce
+ */
+ union GNUNET_CRYPTO_BlindSessionNonce nonce;
+
+ /**
+ * batch issue receipt status state
+ */
+ struct StatusState *ss;
+
+ /**
+ * array position in batch issue receipt request (first position is zero)
+ */
+ size_t position;
};
@@ -157,6 +190,92 @@ issue_receipts_status_cb (void *cls,
/**
+ * Runs phase two, the actual issue receipts operation.
+ * Started once the preparation for CS-donation-units is
+ * done.
+ * @param cls closure.
+ */
+static void
+phase_two (void *cls)
+{
+ struct StatusState *ss = cls;
+ const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps = ss->bkps;
+ ss->birh = DONAU_charity_issue_receipt (
+ TALER_TESTING_interpreter_get_context (ss->is),
+ TALER_TESTING_get_donau_url (ss->is),
+ &ss->charity_priv,
+ ss->charity_id,
+ ss->year,
+ ss->num_bkp,
+ bkps,
+ &issue_receipts_status_cb,
+ ss);
+}
+
+
+/**
+ * Function called when stage 1 of CS issue is finished (request r_pub's)
+ *
+ * @param cls the `struct CSR_Data *`
+ * @param csrresp replies from the /csr-issue request
+ */
+static void
+cs_stage_two_callback (
+ void *cls,
+ const struct DONAU_CsRBatchIssueResponse *csrresp)
+{
+ struct CSR_Data *csr_data = cls;
+ csr_data->csr_handle = NULL;
+ if (csrresp->hr.http_status != MHD_HTTP_CREATED)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected HTTP response code: %d in %s:%u\n",
+ csrresp->hr.http_status,
+ __FILE__,
+ __LINE__);
+ json_dumpf (csrresp->hr.reply,
+ stderr,
+ 0);
+ TALER_TESTING_interpreter_fail (csr_data->ss->is);
+ return;
+ }
+
+ struct DONAU_DonationUnitPublicKey *cs_pk = &csr_data->ss->keys->donation_unit_keys[csr_data->position].key;
+ const struct DONAU_BatchIssueValues *alg_values = csr_data->ss->alg_values[csr_data->position];
+ struct DONAU_BudiMasterSecretP ps;
+ struct DONAU_BlindedUniqueDonorIdentifier *blinded_udi = &csr_data->ss->bkps[csr_data->position].blinded_udi;
+ struct DONAU_UniqueDonorIdentifierHashP *udi_hash = csr_data->ss->h_udis;
+ union GNUNET_CRYPTO_BlindingSecretP *blinding_secret = &csr_data->ss->blinding_secrets[csr_data->position];
+ struct DONAU_UniqueDonorIdentifierNonce *udi_nonce = &csr_data->ss->receipts[csr_data->position].nonce;
+
+ GNUNET_assert (GNUNET_CRYPTO_BSA_CS == cs_pk->bsign_pub_key->cipher);
+ GNUNET_assert (NULL == alg_values);
+ // TODO: write GNUNET method
+ TALER_denom_ewv_copy ((struct TALER_ExchangeWithdrawValues *) alg_values,
+ (struct TALER_ExchangeWithdrawValues *) &csrresp->details.ok.alg_values);
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
+ &ps,
+ sizeof (ps));
+ DONAU_budi_secret_create (&ps,
+ alg_values,
+ blinding_secret);
+ GNUNET_assert (GNUNET_OK ==
+ DONAU_donation_unit_blind (
+ cs_pk,
+ blinding_secret,
+ &csr_data->nonce, /* nonce only needed for cs */
+ udi_nonce,
+ csr_data->ss->h_donor_tax_id,
+ alg_values,
+ udi_hash,
+ blinded_udi));
+ csr_data->ss->cs_pending--;
+ if (0 == csr_data->ss->cs_pending)
+ phase_two (csr_data->ss);
+ GNUNET_free(csr_data);
+}
+
+/**
* Run the command.
*
* @param cls closure.
@@ -227,6 +346,8 @@ status_run (void *cls,
GNUNET_new_array (ss->num_bkp, struct DONAU_UniqueDonorIdentifierHashP);
for (size_t cnt = 0; cnt < ss->num_bkp; cnt++)
{
+ DONAU_donation_unit_pub_hash (&ss->keys->donation_unit_keys[0].key,
+ &ss->bkps[cnt].h_donation_unit_pub);
struct DONAU_UniqueDonorIdentifierNonce *udi_nonce =
&ss->receipts[cnt].nonce;
struct DONAU_BudiMasterSecretP ps;
@@ -234,43 +355,57 @@ status_run (void *cls,
struct DONAU_BlindedUniqueDonorIdentifier *blinded_udi =
&ss->bkps[cnt].blinded_udi;
struct DONAU_UniqueDonorIdentifierHashP *udi_hash = ss->h_udis;
- alg_values = DONAU_donation_unit_ewv_rsa_singleton (); // FIXME: support cs and rsa
-
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
&ps,
sizeof (ps));
- DONAU_budi_secret_create (&ps,
- alg_values,
- &ss->blinding_secrets[cnt]);
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
udi_nonce,
sizeof (*udi_nonce));
- GNUNET_assert (GNUNET_OK ==
- DONAU_donation_unit_blind (
- &ss->keys->donation_unit_keys[0].key,
- &ss->blinding_secrets[cnt],
- NULL, /* no cs-nonce needed for rsa */
- udi_nonce,
- ss->h_donor_tax_id,
- alg_values,
- udi_hash,
- blinded_udi));
- ss->alg_values[cnt] = alg_values;
- DONAU_donation_unit_pub_hash (&ss->keys->donation_unit_keys[0].key,
- &ss->bkps[cnt].h_donation_unit_pub);
+ switch (ss->keys->donation_unit_keys[0].key.bsign_pub_key->cipher)
+ {
+ case GNUNET_CRYPTO_BSA_RSA:
+ alg_values = DONAU_donation_unit_ewv_rsa_singleton ();
+ DONAU_budi_secret_create (&ps,
+ alg_values,
+ &ss->blinding_secrets[cnt]);
+ GNUNET_assert (GNUNET_OK ==
+ DONAU_donation_unit_blind (
+ &ss->keys->donation_unit_keys[0].key,
+ &ss->blinding_secrets[cnt],
+ NULL, /* no cs-nonce needed for rsa */
+ udi_nonce,
+ ss->h_donor_tax_id,
+ alg_values,
+ udi_hash,
+ blinded_udi));
+ ss->alg_values[cnt] = alg_values;
+ break;
+ case GNUNET_CRYPTO_BSA_CS:
+ struct CSR_Data *csr_data = GNUNET_new (struct CSR_Data);
+ TALER_cs_withdraw_nonce_derive ( // TODO: write new method
+ (struct TALER_PlanchetMasterSecretP *) &ps,
+ &csr_data->nonce.cs_nonce);
+ csr_data->ss = ss;
+ csr_data->position = cnt;
+ csr_data->csr_handle = DONAU_csr_issue (
+ TALER_TESTING_interpreter_get_context (is),
+ TALER_TESTING_get_donau_url (is),
+ &ss->keys->donation_unit_keys[0].key,
+ &csr_data->nonce.cs_nonce,
+ &cs_stage_two_callback,
+ csr_data);
+ if (NULL == csr_data->csr_handle)
+ {
+ GNUNET_break (0);
+ }
+ ss->cs_pending++;
+ break;
+ default:
+ GNUNET_break (0);
+ }
}
- const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkps = ss->bkps;
- ss->birh = DONAU_charity_issue_receipt (
- TALER_TESTING_interpreter_get_context (is),
- TALER_TESTING_get_donau_url (is),
- &ss->charity_priv,
- ss->charity_id,
- ss->year,
- ss->num_bkp,
- bkps,
- &issue_receipts_status_cb,
- ss);
-
+ if (0 == ss->cs_pending)
+ phase_two (ss);
}
@@ -345,6 +480,7 @@ issue_receipts_traits (void *cls,
struct TALER_TESTING_Command
TALER_TESTING_cmd_issue_receipts (const char *label,
const char *charity_reference,
+ const bool uses_cs,
const uint64_t year,
const char *donor_tax_id,
const char *salt,
@@ -358,6 +494,7 @@ TALER_TESTING_cmd_issue_receipts (const char *label,
ss->charity_reference = charity_reference;
ss->expected_response_code = expected_response_code;
ss->num_bkp = 3;
+ ss->uses_cs = uses_cs;
struct DONAU_HashDonorTaxId h_donor_tax_id;
struct GNUNET_HashContext *hash_context;
hash_context = GNUNET_CRYPTO_hash_context_start ();