diff options
Diffstat (limited to 'src/lib/exchange_api_refund.c')
-rw-r--r-- | src/lib/exchange_api_refund.c | 338 |
1 files changed, 17 insertions, 321 deletions
diff --git a/src/lib/exchange_api_refund.c b/src/lib/exchange_api_refund.c index 35524ca4b..9159b55f2 100644 --- a/src/lib/exchange_api_refund.c +++ b/src/lib/exchange_api_refund.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2022 Taler Systems SA + Copyright (C) 2014-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 @@ -39,9 +39,9 @@ struct TALER_EXCHANGE_RefundHandle { /** - * The connection to exchange this request handle will use + * The keys of the exchange this request handle will use */ - struct TALER_EXCHANGE_Handle *exchange; + struct TALER_EXCHANGE_Keys *keys; /** * The url for this request. @@ -117,7 +117,6 @@ verify_refund_signature_ok (struct TALER_EXCHANGE_RefundHandle *rh, struct TALER_ExchangePublicKeyP *exchange_pub, struct TALER_ExchangeSignatureP *exchange_sig) { - const struct TALER_EXCHANGE_Keys *key_state; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("exchange_sig", exchange_sig), @@ -134,9 +133,8 @@ verify_refund_signature_ok (struct TALER_EXCHANGE_RefundHandle *rh, GNUNET_break_op (0); return GNUNET_SYSERR; } - key_state = TALER_EXCHANGE_get_keys (rh->exchange); if (GNUNET_OK != - TALER_EXCHANGE_test_signing_key (key_state, + TALER_EXCHANGE_test_signing_key (rh->keys, exchange_pub)) { GNUNET_break_op (0); @@ -160,299 +158,6 @@ verify_refund_signature_ok (struct TALER_EXCHANGE_RefundHandle *rh, /** - * Verify that the information in the "409 Conflict" response - * from the exchange is valid and indeed shows that the refund - * amount requested is too high. - * - * @param[in,out] rh refund handle (refund fee added) - * @param json json reply with the coin transaction history - * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not - */ -static enum GNUNET_GenericReturnValue -verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh, - const json_t *json) -{ - const json_t *history; - struct TALER_DenominationHashP h_denom_pub; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("history", - &history), - GNUNET_JSON_spec_fixed_auto ("h_denom_pub", - &h_denom_pub), - GNUNET_JSON_spec_end () - }; - size_t len; - struct TALER_Amount dtotal; - bool have_deposit; - struct TALER_Amount rtotal; - bool have_refund; - - if (GNUNET_OK != - GNUNET_JSON_parse (json, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - len = json_array_size (history); - if (0 == len) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - have_deposit = false; - have_refund = false; - for (size_t off = 0; off<len; off++) - { - json_t *transaction; - struct TALER_Amount amount; - const char *type; - struct GNUNET_JSON_Specification spec_glob[] = { - TALER_JSON_spec_amount_any ("amount", - &amount), - GNUNET_JSON_spec_string ("type", - &type), - GNUNET_JSON_spec_end () - }; - - transaction = json_array_get (history, - off); - if (GNUNET_OK != - GNUNET_JSON_parse (transaction, - spec_glob, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (0 == strcasecmp (type, - "DEPOSIT")) - { - struct TALER_Amount deposit_fee; - struct TALER_MerchantWireHashP h_wire; - struct TALER_PrivateContractHashP h_contract_terms; - struct TALER_AgeCommitmentHash h_age_commitment; - bool no_hac; - struct TALER_ExtensionPolicyHashP h_policy; - bool no_h_policy; - struct GNUNET_TIME_Timestamp wallet_timestamp; - struct TALER_MerchantPublicKeyP merchant_pub; - struct GNUNET_TIME_Timestamp refund_deadline; - struct TALER_CoinSpendSignatureP sig; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_fixed_auto ("coin_sig", - &sig), - GNUNET_JSON_spec_fixed_auto ("h_contract_terms", - &h_contract_terms), - GNUNET_JSON_spec_fixed_auto ("h_wire", - &h_wire), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_fixed_auto ("h_age_commitment", - &h_age_commitment), - &no_hac), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_fixed_auto ("h_policy", - &h_policy), - &no_h_policy), - GNUNET_JSON_spec_timestamp ("timestamp", - &wallet_timestamp), - GNUNET_JSON_spec_timestamp ("refund_deadline", - &refund_deadline), - TALER_JSON_spec_amount_any ("deposit_fee", - &deposit_fee), - GNUNET_JSON_spec_fixed_auto ("merchant_pub", - &merchant_pub), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (transaction, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - TALER_wallet_deposit_verify (&amount, - &deposit_fee, - &h_wire, - &h_contract_terms, - no_hac ? NULL : &h_age_commitment, - no_h_policy ? NULL: &h_policy, - &h_denom_pub, - wallet_timestamp, - &merchant_pub, - refund_deadline, - &rh->coin_pub, - &sig)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if ( (0 != GNUNET_memcmp (&rh->h_contract_terms, - &h_contract_terms)) || - (0 != GNUNET_memcmp (&rh->merchant, - &merchant_pub)) ) - { - /* deposit information is about a different merchant/contract */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (have_deposit) - { - /* this cannot really happen, but we conservatively support it anyway */ - if (GNUNET_YES != - TALER_amount_cmp_currency (&amount, - &dtotal)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - GNUNET_break (0 <= - TALER_amount_add (&dtotal, - &dtotal, - &amount)); - } - else - { - dtotal = amount; - have_deposit = true; - } - } - else if (0 == strcasecmp (type, - "REFUND")) - { - struct TALER_MerchantSignatureP sig; - struct TALER_Amount refund_fee; - struct TALER_Amount sig_amount; - struct TALER_PrivateContractHashP h_contract_terms; - uint64_t rtransaction_id; - struct TALER_MerchantPublicKeyP merchant_pub; - struct GNUNET_JSON_Specification ispec[] = { - TALER_JSON_spec_amount_any ("refund_fee", - &refund_fee), - GNUNET_JSON_spec_fixed_auto ("merchant_sig", - &sig), - GNUNET_JSON_spec_fixed_auto ("h_contract_terms", - &h_contract_terms), - GNUNET_JSON_spec_fixed_auto ("merchant_pub", - &merchant_pub), - GNUNET_JSON_spec_uint64 ("rtransaction_id", - &rtransaction_id), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (transaction, - ispec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (0 > - TALER_amount_add (&sig_amount, - &refund_fee, - &amount)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - TALER_merchant_refund_verify (&rh->coin_pub, - &h_contract_terms, - rtransaction_id, - &sig_amount, - &merchant_pub, - &sig)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if ( (0 != GNUNET_memcmp (&rh->h_contract_terms, - &h_contract_terms)) || - (0 != GNUNET_memcmp (&rh->merchant, - &merchant_pub)) ) - { - /* refund is about a different merchant/contract */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - if (rtransaction_id == rh->rtransaction_id) - { - /* Eh, this shows either a dependency failure or idempotency, - but must not happen in a conflict reply. Fail! */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - if (have_refund) - { - if (GNUNET_YES != - TALER_amount_cmp_currency (&amount, - &rtotal)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - GNUNET_break (0 <= - TALER_amount_add (&rtotal, - &rtotal, - &amount)); - } - else - { - rtotal = amount; - have_refund = true; - } - } - else - { - /* unexpected type, new version on server? */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected type `%s' in response for exchange refund\n", - type); - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - - if (have_refund) - { - if (0 > - TALER_amount_add (&rtotal, - &rtotal, - &rh->refund_amount)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - } - else - { - rtotal = rh->refund_amount; - have_refund = true; - } - if (! have_deposit) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (-1 != TALER_amount_cmp (&dtotal, - &rtotal)) - { - /* rtotal <= dtotal is fine, no conflict! */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - /* dtotal < rtotal: that's a conflict! */ - return GNUNET_OK; -} - - -/** * Verify that the information on the "412 Dependency Failed" response * from the exchange is valid and indeed shows that there is a refund * transaction ID reuse going on. @@ -609,19 +314,6 @@ handle_refund_finished (void *cls, break; case MHD_HTTP_CONFLICT: /* Requested total refunds exceed deposited amount */ - if (GNUNET_OK != - verify_conflict_history_ok (rh, - j)) - { - GNUNET_break (0); - json_dumpf (j, - stderr, - JSON_INDENT (2)); - rr.hr.http_status = 0; - rr.hr.ec = TALER_EC_EXCHANGE_REFUND_INVALID_FAILURE_PROOF_BY_EXCHANGE; - rr.hr.hint = "conflict information provided by exchange is invalid"; - break; - } rr.hr.ec = TALER_JSON_get_error_code (j); rr.hr.hint = TALER_JSON_get_error_hint (j); break; @@ -631,6 +323,10 @@ handle_refund_finished (void *cls, rr.hr.ec = TALER_JSON_get_error_code (j); rr.hr.hint = TALER_JSON_get_error_hint (j); break; + case MHD_HTTP_FAILED_DEPENDENCY: + rr.hr.ec = TALER_JSON_get_error_code (j); + rr.hr.hint = TALER_JSON_get_error_hint (j); + break; case MHD_HTTP_PRECONDITION_FAILED: if (GNUNET_OK != verify_failed_dependency_ok (rh, @@ -672,7 +368,9 @@ handle_refund_finished (void *cls, struct TALER_EXCHANGE_RefundHandle * TALER_EXCHANGE_refund ( - struct TALER_EXCHANGE_Handle *exchange, + struct GNUNET_CURL_Context *ctx, + const char *url, + struct TALER_EXCHANGE_Keys *keys, const struct TALER_Amount *amount, const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_CoinSpendPublicKeyP *coin_pub, @@ -684,13 +382,10 @@ TALER_EXCHANGE_refund ( struct TALER_MerchantPublicKeyP merchant_pub; struct TALER_MerchantSignatureP merchant_sig; struct TALER_EXCHANGE_RefundHandle *rh; - struct GNUNET_CURL_Context *ctx; json_t *refund_obj; CURL *eh; char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32]; - GNUNET_assert (GNUNET_YES == - TEAH_handle_is_ready (exchange)); GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv, &merchant_pub.eddsa_pub); TALER_merchant_refund_sign (coin_pub, @@ -711,7 +406,7 @@ TALER_EXCHANGE_refund ( *end = '\0'; GNUNET_snprintf (arg_str, sizeof (arg_str), - "/coins/%s/refund", + "coins/%s/refund", pub_str); } refund_obj = GNUNET_JSON_PACK ( @@ -726,11 +421,11 @@ TALER_EXCHANGE_refund ( GNUNET_JSON_pack_data_auto ("merchant_sig", &merchant_sig)); rh = GNUNET_new (struct TALER_EXCHANGE_RefundHandle); - rh->exchange = exchange; rh->cb = cb; rh->cb_cls = cb_cls; - rh->url = TEAH_path_to_url (exchange, - arg_str); + rh->url = TALER_url_join (url, + arg_str, + NULL); if (NULL == rh->url) { json_decref (refund_obj); @@ -761,7 +456,7 @@ TALER_EXCHANGE_refund ( GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "URL for refund: `%s'\n", rh->url); - ctx = TEAH_handle_to_context (exchange); + rh->keys = TALER_EXCHANGE_keys_incref (keys); rh->job = GNUNET_CURL_job_add2 (ctx, eh, rh->ctx.headers, @@ -781,6 +476,7 @@ TALER_EXCHANGE_refund_cancel (struct TALER_EXCHANGE_RefundHandle *refund) } GNUNET_free (refund->url); TALER_curl_easy_post_finished (&refund->ctx); + TALER_EXCHANGE_keys_decref (refund->keys); GNUNET_free (refund); } |