diff options
author | Christian Blättler <blatc2@bfh.ch> | 2024-04-27 10:12:16 +0200 |
---|---|---|
committer | Christian Blättler <blatc2@bfh.ch> | 2024-04-27 10:12:24 +0200 |
commit | d50651cdfa34213d33c5f8651cbeeba10cc37cfd (patch) | |
tree | e83c4bf82b46a6aa75c03b669f697d2b1128d564 | |
parent | 0df606b95f01408d39adc5b0f24a21b6962950d2 (diff) |
work on testing orders with tokens
-rw-r--r-- | src/include/taler_merchant_service.h | 43 | ||||
-rw-r--r-- | src/include/taler_merchant_testing_lib.h | 4 | ||||
-rw-r--r-- | src/lib/merchant_api_post_order_pay.c | 77 | ||||
-rw-r--r-- | src/testing/test_merchant_api.c | 45 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_pay_order.c | 63 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_post_orders.c | 23 |
6 files changed, 230 insertions, 25 deletions
diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h index 180df516..e3d65188 100644 --- a/src/include/taler_merchant_service.h +++ b/src/include/taler_merchant_service.h @@ -3086,6 +3086,39 @@ TALER_MERCHANT_order_claim_cancel (struct TALER_MERCHANT_OrderClaimHandle *och); /** + * All the details about a token that are generated during issuance and + * that may be needed for future operations on the coin. + */ +struct TALER_MERCHANT_PrivateTokenDetails +{ + /** + * Private key of the token. + */ + struct TALER_TokenUsePrivateKeyP token_priv; + + /** + * Value used to blind the key for the signature. + */ + union GNUNET_CRYPTO_BlindingSecretP blinding_secret; + + /** + * Unblinded token issue signature made by the merchant. + */ + struct TALER_TokenIssueSignatureP issue_sig; + + /** + * Token issue public key. + */ + struct TALER_TokenIssuePublicKeyP issue_pub; + + /** + * Inputs needed from the merchant for blind signing. + */ + struct GNUNET_CRYPTO_BlindingInputValues *blinding_inputs; + +}; + +/** * @brief Handle to a POST /orders/$ID/pay operation at a merchant. Note that * we use the same handle for interactions with frontends (API for wallets) or * backends (API for frontends). The difference is that for the frontend API, @@ -3128,6 +3161,16 @@ struct TALER_MERCHANT_PayResponse */ const char *pos_confirmation; + /** + * Array of tokens that were issued for the payment. + */ + struct TALER_MERCHANT_PrivateTokenDetails *tokens; + + /** + * Length of the @e tokens array. + */ + unsigned int num_tokens; + } ok; // TODO: might want to return further details on errors, diff --git a/src/include/taler_merchant_testing_lib.h b/src/include/taler_merchant_testing_lib.h index 12d38fb5..20ac3076 100644 --- a/src/include/taler_merchant_testing_lib.h +++ b/src/include/taler_merchant_testing_lib.h @@ -614,6 +614,8 @@ TALER_TESTING_cmd_merchant_post_orders3 ( * the proposal request. * @param http_status expected HTTP status. * @param token_family_reference label of the POST /tokenfamilies cmd. + * @param num_inputs number of input tokens. + * @param num_outputs number of output tokens. * @param order_id the name of the order to add. * @param refund_deadline the deadline for refunds on this order. * @param pay_deadline the deadline for payment on this order. @@ -628,6 +630,8 @@ TALER_TESTING_cmd_merchant_post_orders_choices ( const char *merchant_url, unsigned int http_status, const char *token_family_reference, + unsigned int num_inputs, + unsigned int num_outputs, const char *order_id, struct GNUNET_TIME_Timestamp refund_deadline, struct GNUNET_TIME_Timestamp pay_deadline, diff --git a/src/lib/merchant_api_post_order_pay.c b/src/lib/merchant_api_post_order_pay.c index e56bac60..c74113b6 100644 --- a/src/lib/merchant_api_post_order_pay.c +++ b/src/lib/merchant_api_post_order_pay.c @@ -25,6 +25,8 @@ */ #include "platform.h" #include <curl/curl.h> +#include <gnunet/gnunet_common.h> +#include <gnunet/gnunet_json_lib.h> #include <jansson.h> #include <microhttpd.h> /* just for HTTP status codes */ #include <gnunet/gnunet_util_lib.h> @@ -80,7 +82,7 @@ struct TALER_MERCHANT_OrderPayHandle struct TALER_MERCHANT_PaidCoin *coins; /** - * The tokens we are using. + * The tokens we are using as inputs. */ struct TALER_MERCHANT_UsedToken *tokens; @@ -133,6 +135,48 @@ struct TALER_MERCHANT_OrderPayHandle /** + * Parse blindly signed output tokens from response. + */ +static enum GNUNET_GenericReturnValue +parse_tokens (const json_t *token_sigs, + struct TALER_MERCHANT_PrivateTokenDetails **tokens, + unsigned int *num_tokens) +{ + GNUNET_array_grow (*tokens, + *num_tokens, + json_array_size (token_sigs)); + + for (unsigned int i = 0; i<(*num_tokens); i++) + { + struct TALER_MERCHANT_PrivateTokenDetails *token = &(*tokens)[i]; + const json_t *js = json_array_get (token_sigs, + i); + + if (NULL == js) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_token_issue_sig ("blind_sig", + &token->issue_sig), + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (js, + spec, + NULL, NULL)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + } + + return GNUNET_NO; +} + + +/** * Function called when we're done processing the * HTTP /pay request. * @@ -164,15 +208,17 @@ handle_pay_finished (void *cls, case MHD_HTTP_OK: if (oph->am_wallet) { - /* Here we can (and should) verify the merchant's signature */ + const json_t *token_sigs = NULL; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ( - "sig", - &pr.details.ok.merchant_sig), + GNUNET_JSON_spec_fixed_auto ("sig", + &pr.details.ok.merchant_sig), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ( - "pos_confirmation", - &pr.details.ok.pos_confirmation), + GNUNET_JSON_spec_string ("pos_confirmation", + &pr.details.ok.pos_confirmation), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ("token_sigs", + &token_sigs), NULL), GNUNET_JSON_spec_end () }; @@ -189,6 +235,21 @@ handle_pay_finished (void *cls, break; } + if (NULL != token_sigs) + { + if (GNUNET_OK != + parse_tokens (token_sigs, + &pr.details.ok.tokens, + &pr.details.ok.num_tokens)) + { + GNUNET_break_op (0); + pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + pr.hr.http_status = 0; + pr.hr.hint = "failed to parse token_sigs field in response"; + break; + } + } + if (GNUNET_OK != TALER_merchant_pay_verify (&oph->h_contract_terms, &oph->merchant_pub, diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c index e71d0002..9f687588 100644 --- a/src/testing/test_merchant_api.c +++ b/src/testing/test_merchant_api.c @@ -1693,23 +1693,48 @@ run (void *cls, GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_YEARS), GNUNET_TIME_UNIT_MONTHS, "subscription"), - TALER_TESTING_cmd_merchant_post_orders_choices ("create-order-with-choices", + TALER_TESTING_cmd_merchant_post_orders_choices ("create-order-with-output", cred.cfg, merchant_url, MHD_HTTP_OK, "create-tokenfamily", - "5-choices", + 0, + 1, + "5-output", GNUNET_TIME_UNIT_ZERO_TS, GNUNET_TIME_UNIT_FOREVER_TS, "EUR:5.0"), - TALER_TESTING_cmd_merchant_pay_order ("pay-order-with-choices", - merchant_url, - MHD_HTTP_OK, - "create-order-with-choices", - "withdraw-coin-1", - "EUR:5", - "EUR:4.99", - NULL), + TALER_TESTING_cmd_merchant_pay_order_choices ("pay-order-with-output", + merchant_url, + MHD_HTTP_OK, + "create-order-with-output", + "withdraw-coin-1", + "EUR:5", + "EUR:4.99", + NULL, + 0, + NULL), + TALER_TESTING_cmd_merchant_post_orders_choices ("create-order-with-input-and-output", + cred.cfg, + merchant_url, + MHD_HTTP_OK, + "create-tokenfamily", + 1, + 1, + "5-input-output", + GNUNET_TIME_UNIT_ZERO_TS, + GNUNET_TIME_UNIT_FOREVER_TS, + "EUR:0.0"), + TALER_TESTING_cmd_merchant_pay_order_choices ("pay-order-with-output", + merchant_url, + MHD_HTTP_OK, + "create-order-with-output", + "withdraw-coin-1", + "EUR:5", + "EUR:4.99", + NULL, + 0, + "pay-order-with-output"), TALER_TESTING_cmd_end () }; diff --git a/src/testing/testing_api_cmd_pay_order.c b/src/testing/testing_api_cmd_pay_order.c index 071a9a23..12653ebc 100644 --- a/src/testing/testing_api_cmd_pay_order.c +++ b/src/testing/testing_api_cmd_pay_order.c @@ -65,6 +65,13 @@ struct PayState const char *coin_reference; /** + * Reference to a command that can provide one or + * multiple tokens used as inputs for the payment. + * In the form "LABEL0[/INDEX];LABEL1[/INDEX];..." + */ + const char *token_reference; + + /** * The merchant base URL. */ const char *merchant_url; @@ -95,6 +102,16 @@ struct PayState struct TALER_MerchantSignatureP merchant_sig; /** + * Array of issued tokens, set on success. + */ + struct TALER_MERCHANT_PrivateTokenDetails *issued_tokens; + + /** + * Number of tokens in @e issued_tokens. + */ + unsigned int num_issued_tokens; + + /** * The session for which the payment is made. */ const char *session_id; @@ -349,15 +366,18 @@ pay_cb (void *cls, if (ps->http_status != pr->hr.http_status) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u (%d) to command %s\n", + "Unexpected response code %u (%d) to command (%s) %s\n", pr->hr.http_status, (int) pr->hr.ec, + pr->hr.hint, TALER_TESTING_interpreter_get_current_label (ps->is)); TALER_TESTING_FAIL (ps->is); } if (MHD_HTTP_OK == pr->hr.http_status) { ps->merchant_sig = pr->details.ok.merchant_sig; + ps->issued_tokens = pr->details.ok.tokens; + ps->num_issued_tokens = pr->details.ok.num_tokens; if (NULL != ps->pos_key) { char *pc; @@ -426,6 +446,8 @@ pay_run (void *cls, unsigned int error_line = 0; struct TALER_MERCHANT_PayCoin *pay_coins; unsigned int npay_coins; + struct TALER_MERCHANT_UseToken *use_tokens = NULL; + unsigned int len_use_tokens = 0; const struct TALER_MerchantSignatureP *merchant_sig; const enum TALER_MerchantConfirmationAlgorithm *alg_ptr; @@ -518,6 +540,25 @@ pay_run (void *cls, } GNUNET_free (cr); } + if (NULL != ps->token_reference) + { + char *tr; + + tr = GNUNET_strdup (ps->token_reference); + if (GNUNET_OK != + build_tokens (&use_tokens, + &len_use_tokens, + tr, + is)) + { + GNUNET_array_grow (use_tokens, + len_use_tokens, + 0); + GNUNET_free (tr); + TALER_TESTING_FAIL (is); + } + GNUNET_free (tr); + } if (GNUNET_OK != TALER_TESTING_get_trait_merchant_sig (proposal_cmd, &merchant_sig)) @@ -545,8 +586,8 @@ pay_run (void *cls, order_id, npay_coins, pay_coins, - 0, - NULL, + len_use_tokens, + use_tokens, &pay_cb, ps); GNUNET_array_grow (pay_coins, @@ -603,6 +644,13 @@ pay_traits (void *cls, const struct TALER_TESTING_Command *proposal_cmd; const struct TALER_MerchantPublicKeyP *merchant_pub; + if (NULL != ps->token_reference && + index >= ps->num_issued_tokens) + { + GNUNET_break (0); + return GNUNET_NO; + } + if (NULL == (proposal_cmd = TALER_TESTING_interpreter_lookup_command (ps->is, @@ -644,6 +692,12 @@ pay_traits (void *cls, TALER_TESTING_make_trait_amount (&amount_with_fee), TALER_TESTING_make_trait_otp_key (ps->pos_key), TALER_TESTING_make_trait_otp_alg (&ps->pos_alg), + TALER_TESTING_make_trait_token_priv (index, + &ps->issued_tokens[index].token_priv), + TALER_TESTING_make_trait_token_issue_pub (index, + &ps->issued_tokens[index].issue_pub), + TALER_TESTING_make_trait_token_issue_sig (index, + &ps->issued_tokens[index].issue_sig), TALER_TESTING_trait_end () }; @@ -666,7 +720,7 @@ TALER_TESTING_cmd_merchant_pay_order_choices (const char *label, const char *amount_without_fee, const char *session_id, int choice_index, - const char *input_reference) + const char *token_reference) { struct PayState *ps; @@ -678,6 +732,7 @@ TALER_TESTING_cmd_merchant_pay_order_choices (const char *label, ps->amount_with_fee = amount_with_fee; ps->amount_without_fee = amount_without_fee; ps->session_id = session_id; + ps->token_reference = token_reference; if (0 <= choice_index) { ps->wallet_data = json_pack ("{s:i}", diff --git a/src/testing/testing_api_cmd_post_orders.c b/src/testing/testing_api_cmd_post_orders.c index 8f7bd46d..a0f2941c 100644 --- a/src/testing/testing_api_cmd_post_orders.c +++ b/src/testing/testing_api_cmd_post_orders.c @@ -65,6 +65,18 @@ struct OrdersState const char *token_family_reference; /** + * How many tokens of the token family created in + * @a token_family_reference are required as inputs. + */ + unsigned int num_inputs; + + /** + * How many tokens of the token family created in + * @a token_family_reference should be issued as outputs. + */ + unsigned int num_outputs; + + /** * Contract terms obtained from the backend. */ json_t *contract_terms; @@ -657,9 +669,10 @@ orders_run3 (void *cls, TALER_TESTING_FAIL (is); } make_choices_json (slug, slug, - 1, 1, - GNUNET_TIME_absolute_to_timestamp(now), - GNUNET_TIME_absolute_to_timestamp(now), + ps->num_inputs, + ps->num_outputs, + GNUNET_TIME_absolute_to_timestamp (now), + GNUNET_TIME_absolute_to_timestamp (now), &ps->choices); GNUNET_assert (0 == @@ -962,6 +975,8 @@ TALER_TESTING_cmd_merchant_post_orders_choices ( const char *merchant_url, unsigned int http_status, const char *token_family_reference, + unsigned int num_inputs, + unsigned int num_outputs, const char *order_id, struct GNUNET_TIME_Timestamp refund_deadline, struct GNUNET_TIME_Timestamp pay_deadline, @@ -978,6 +993,8 @@ TALER_TESTING_cmd_merchant_post_orders_choices ( &ps->order_terms); ps->http_status = http_status; ps->token_family_reference = token_family_reference; + ps->num_inputs = num_inputs; + ps->num_outputs = num_outputs; ps->expected_order_id = order_id; ps->merchant_url = merchant_url; ps->with_claim = true; |