v12: also do not sign over merchant_pub in REFUND signature, centralize logic

This commit is contained in:
Christian Grothoff 2021-12-25 14:58:04 +01:00
parent 1c34489905
commit 84c9adf5a6
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
9 changed files with 228 additions and 158 deletions

View File

@ -1794,41 +1794,30 @@ refund_cb (void *cls,
}
/* verify refund signature */
if (GNUNET_OK !=
TALER_merchant_refund_verify (coin_pub,
h_contract_terms,
rtransaction_id,
amount_with_fee,
merchant_pub,
merchant_sig))
{
struct TALER_RefundRequestPS rr = {
.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
.purpose.size = htonl (sizeof (rr)),
.h_contract_terms = *h_contract_terms,
.coin_pub = *coin_pub,
.merchant = *merchant_pub,
.rtransaction_id = GNUNET_htonll (rtransaction_id),
};
TALER_amount_hton (&rr.refund_amount,
amount_with_fee);
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
&rr,
&merchant_sig->eddsa_sig,
&merchant_pub->eddsa_pub))
{
TALER_ARL_report (report_bad_sig_losses,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("operation",
"refund"),
GNUNET_JSON_pack_uint64 ("row",
rowid),
TALER_JSON_pack_amount ("loss",
amount_with_fee),
GNUNET_JSON_pack_data_auto ("coin_pub",
coin_pub)));
TALER_ARL_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
amount_with_fee);
if (TALER_ARL_do_abort ())
return GNUNET_SYSERR;
return GNUNET_OK;
}
TALER_ARL_report (report_bad_sig_losses,
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("operation",
"refund"),
GNUNET_JSON_pack_uint64 ("row",
rowid),
TALER_JSON_pack_amount ("loss",
amount_with_fee),
GNUNET_JSON_pack_data_auto ("coin_pub",
coin_pub)));
TALER_ARL_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
amount_with_fee);
if (TALER_ARL_do_abort ())
return GNUNET_SYSERR;
return GNUNET_OK;
}
TALER_amount_ntoh (&refund_fee,

View File

@ -209,31 +209,19 @@ verify_and_execute_refund (struct MHD_Connection *connection,
.refund = refund
};
// FIXME: move to libtalerutil!
if (GNUNET_OK !=
TALER_merchant_refund_verify (&refund->coin.coin_pub,
&refund->details.h_contract_terms,
refund->details.rtransaction_id,
&refund->details.refund_amount,
&refund->details.merchant_pub,
&refund->details.merchant_sig))
{
struct TALER_RefundRequestPS rr = {
.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
.purpose.size = htonl (sizeof (rr)),
.h_contract_terms = refund->details.h_contract_terms,
.coin_pub = refund->coin.coin_pub,
.merchant = refund->details.merchant_pub,
.rtransaction_id = GNUNET_htonll (refund->details.rtransaction_id)
};
TALER_amount_hton (&rr.refund_amount,
&refund->details.refund_amount);
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
&rr,
&refund->details.merchant_sig.eddsa_sig,
&refund->details.merchant_pub.eddsa_pub))
{
TALER_LOG_WARNING ("Invalid signature on refund request\n");
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_EXCHANGE_REFUND_MERCHANT_SIGNATURE_INVALID,
NULL);
}
TALER_LOG_WARNING ("Invalid signature on refund request\n");
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_FORBIDDEN,
TALER_EC_EXCHANGE_REFUND_MERCHANT_SIGNATURE_INVALID,
NULL);
}
/* Fetch the coin's denomination (hash) */

View File

@ -165,25 +165,15 @@ TEH_RESPONSE_compile_transaction_history (
const struct TALER_EXCHANGEDB_RefundListEntry *refund =
pos->details.refund;
struct TALER_Amount value;
// FIXME: move to libtalerutil!
struct TALER_RefundRequestPS rr = {
.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
.purpose.size = htonl (sizeof (rr)),
.h_contract_terms = refund->h_contract_terms,
.coin_pub = *coin_pub,
.merchant = refund->merchant_pub,
.rtransaction_id = GNUNET_htonll (refund->rtransaction_id)
};
TALER_amount_hton (&rr.refund_amount,
&refund->refund_amount);
#if ENABLE_SANITY_CHECKS
/* internal sanity check before we hand out a bogus sig... */
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
&rr,
&refund->merchant_sig.eddsa_sig,
&refund->merchant_pub.eddsa_pub))
TALER_merchant_refund_verify (coin_pub,
&refund->h_contract_terms,
refund->rtransaction_id,
&refund->refund_amount,
&refund->merchant_pub,
&refund->merchant_sig))
{
GNUNET_break (0);
json_decref (history);

View File

@ -1897,6 +1897,50 @@ TALER_wallet_recoup_refresh_sign (
struct TALER_CoinSpendSignatureP *coin_sig);
/* ********************* merchant signing ************************** */
/**
* Create merchant signature approving a refund.
*
* @param coin_pub coin to be refunded
* @param h_contract_terms contract to be refunded
* @param rtransaction_id unique ID for this (partial) refund
* @param amount amount to be refunded
* @param merchant_priv private key to sign with
* @param[out] merchant_sig where to write the signature
*/
void
TALER_merchant_refund_sign (
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_PrivateContractHash *h_contract_terms,
uint64_t rtransaction_id,
const struct TALER_Amount *amount,
const struct TALER_MerchantPrivateKeyP *merchant_priv,
struct TALER_MerchantSignatureP *merchant_sig);
/**
* Verify merchant signature approving a refund.
*
* @param coin_pub coin to be refunded
* @param h_contract_terms contract to be refunded
* @param rtransaction_id unique ID for this (partial) refund
* @param amount amount to be refunded
* @param merchant_pub public key of the merchant
* @param merchant_sig signature to verify
* @return #GNUNET_OK if the signature is valid
*/
enum GNUNET_GenericReturnValue
TALER_merchant_refund_verify (
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_PrivateContractHash *h_contract_terms,
uint64_t rtransaction_id,
const struct TALER_Amount *amount,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_MerchantSignatureP *merchant_sig);
/* ********************* offline signing ************************** */

View File

@ -630,12 +630,6 @@ struct TALER_RefundRequestPS
*/
struct TALER_CoinSpendPublicKeyP coin_pub;
/**
* The Merchant's public key. Allows the merchant to later refund
* the transaction or to inquire about the wire transfer identifier.
*/
struct TALER_MerchantPublicKeyP merchant;
/**
* Merchant-generated transaction ID for the refund.
*/

View File

@ -598,25 +598,23 @@ TALER_EXCHANGE_verify_coin_history (
else if (0 == strcasecmp (type,
"REFUND"))
{
struct TALER_PrivateContractHash h_contract_terms;
struct TALER_MerchantPublicKeyP merchant_pub;
struct TALER_MerchantSignatureP sig;
struct TALER_Amount refund_fee;
struct TALER_Amount sig_amount;
struct TALER_RefundRequestPS rr = {
.purpose.size = htonl (sizeof (rr)),
.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
.coin_pub = *coin_pub
};
uint64_t rtransaction_id;
struct GNUNET_JSON_Specification spec[] = {
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",
&rr.h_contract_terms),
&h_contract_terms),
GNUNET_JSON_spec_fixed_auto ("merchant_pub",
&rr.merchant),
&merchant_pub),
GNUNET_JSON_spec_uint64 ("rtransaction_id",
&rr.rtransaction_id),
&rtransaction_id),
GNUNET_JSON_spec_end ()
};
@ -636,16 +634,13 @@ TALER_EXCHANGE_verify_coin_history (
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
TALER_amount_hton (&rr.refund_amount,
&sig_amount);
rr.rtransaction_id = GNUNET_htonll (rr.rtransaction_id);
TALER_amount_hton (&rr.refund_amount,
&sig_amount);
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
&rr,
&sig.eddsa_sig,
&rr.merchant.eddsa_pub))
TALER_merchant_refund_verify (coin_pub,
&h_contract_terms,
rtransaction_id,
&sig_amount,
&merchant_pub,
&sig))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;

View File

@ -95,8 +95,10 @@ verify_refund_signature_ok (struct TALER_EXCHANGE_RefundHandle *rh,
{
const struct TALER_EXCHANGE_Keys *key_state;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("exchange_sig", exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub", exchange_pub),
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
exchange_pub),
GNUNET_JSON_spec_end ()
};
@ -291,22 +293,20 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
struct TALER_MerchantSignatureP sig;
struct TALER_Amount refund_fee;
struct TALER_Amount sig_amount;
struct TALER_RefundRequestPS rr = {
.purpose.size = htonl (sizeof (rr)),
.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
.coin_pub = rh->depconf.coin_pub
};
struct TALER_PrivateContractHash 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",
&rr.h_contract_terms),
&h_contract_terms),
GNUNET_JSON_spec_fixed_auto ("merchant_pub",
&rr.merchant),
&merchant_pub),
GNUNET_JSON_spec_uint64 ("rtransaction_id",
&rr.rtransaction_id), /* Note: converted to NBO below */
&rtransaction_id),
GNUNET_JSON_spec_end ()
};
@ -328,30 +328,29 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
TALER_amount_hton (&rr.refund_amount,
&sig_amount);
rr.rtransaction_id = GNUNET_htonll (rr.rtransaction_id);
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
&rr,
&sig.eddsa_sig,
&rr.merchant.eddsa_pub))
TALER_merchant_refund_verify (&rh->depconf.coin_pub,
&h_contract_terms,
rtransaction_id,
&sig_amount,
&merchant_pub,
&sig))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
if ( (0 != GNUNET_memcmp (&rh->depconf.h_contract_terms,
&rr.h_contract_terms)) ||
&h_contract_terms)) ||
(0 != GNUNET_memcmp (&rh->depconf.merchant,
&rr.merchant)) )
&merchant_pub)) )
{
/* refund is about a different merchant/contract */
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
if (rr.rtransaction_id == rh->depconf.rtransaction_id)
if (rtransaction_id == rh->depconf.rtransaction_id)
{
/* Eh, this shows either a dependency failure or idempotency,
but must not happen in a conflict reply. Fail! */
@ -468,15 +467,13 @@ verify_failed_dependency_ok (struct TALER_EXCHANGE_RefundHandle *rh,
e = json_array_get (h, 0);
{
struct TALER_Amount amount;
struct TALER_Amount depconf_amount;
const char *type;
struct TALER_MerchantSignatureP sig;
struct TALER_Amount refund_fee;
struct TALER_RefundRequestPS rr = {
.purpose.size = htonl (sizeof (rr)),
.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
.coin_pub = rh->depconf.coin_pub
};
struct TALER_PrivateContractHash h_contract_terms;
uint64_t rtransaction_id;
struct TALER_MerchantPublicKeyP merchant_pub;
struct GNUNET_JSON_Specification ispec[] = {
TALER_JSON_spec_amount_any ("amount",
&amount),
@ -487,9 +484,9 @@ verify_failed_dependency_ok (struct TALER_EXCHANGE_RefundHandle *rh,
GNUNET_JSON_spec_fixed_auto ("merchant_sig",
&sig),
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
&rr.h_contract_terms),
&h_contract_terms),
GNUNET_JSON_spec_fixed_auto ("merchant_pub",
&rr.merchant),
&merchant_pub),
GNUNET_JSON_spec_uint64 ("rtransaction_id",
&rtransaction_id),
GNUNET_JSON_spec_end ()
@ -504,26 +501,27 @@ verify_failed_dependency_ok (struct TALER_EXCHANGE_RefundHandle *rh,
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
rr.rtransaction_id = GNUNET_htonll (rtransaction_id);
TALER_amount_hton (&rr.refund_amount,
&amount);
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
&rr,
&sig.eddsa_sig,
&rh->depconf.merchant.eddsa_pub))
TALER_merchant_refund_verify (&rh->depconf.coin_pub,
&h_contract_terms,
rtransaction_id,
&amount,
&merchant_pub,
&sig))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
if ( (rr.rtransaction_id != rh->depconf.rtransaction_id) ||
TALER_amount_ntoh (&depconf_amount,
&rh->depconf.refund_amount);
if ( (rtransaction_id != rh->depconf.rtransaction_id) ||
(0 != GNUNET_memcmp (&rh->depconf.h_contract_terms,
&rr.h_contract_terms)) ||
&h_contract_terms)) ||
(0 != GNUNET_memcmp (&rh->depconf.merchant,
&rr.merchant)) ||
(0 == TALER_amount_cmp_nbo (&rh->depconf.refund_amount,
&rr.refund_amount)) )
&merchant_pub)) ||
(0 == TALER_amount_cmp (&depconf_amount,
&amount)) )
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
@ -675,13 +673,7 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
TALER_EXCHANGE_RefundCallback cb,
void *cb_cls)
{
struct TALER_RefundRequestPS rr = {
.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
.purpose.size = htonl (sizeof (rr)),
.h_contract_terms = *h_contract_terms,
.rtransaction_id = GNUNET_htonll (rtransaction_id),
.coin_pub = *coin_pub
};
struct TALER_MerchantPublicKeyP merchant_pub;
struct TALER_MerchantSignatureP merchant_sig;
struct TALER_EXCHANGE_RefundHandle *rh;
struct GNUNET_CURL_Context *ctx;
@ -692,23 +684,22 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
GNUNET_assert (GNUNET_YES ==
TEAH_handle_is_ready (exchange));
GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv,
&rr.merchant.eddsa_pub);
TALER_amount_hton (&rr.refund_amount,
amount);
GNUNET_CRYPTO_eddsa_sign (&merchant_priv->eddsa_priv,
&rr,
&merchant_sig.eddsa_sig);
&merchant_pub.eddsa_pub);
TALER_merchant_refund_sign (coin_pub,
h_contract_terms,
rtransaction_id,
amount,
merchant_priv,
&merchant_sig);
{
char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
char *end;
end = GNUNET_STRINGS_data_to_string (coin_pub,
sizeof (struct
TALER_CoinSpendPublicKeyP),
pub_str,
sizeof (pub_str));
end = GNUNET_STRINGS_data_to_string (
coin_pub,
sizeof (struct TALER_CoinSpendPublicKeyP),
pub_str,
sizeof (pub_str));
*end = '\0';
GNUNET_snprintf (arg_str,
sizeof (arg_str),
@ -723,7 +714,7 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
GNUNET_JSON_pack_uint64 ("rtransaction_id",
rtransaction_id),
GNUNET_JSON_pack_data_auto ("merchant_pub",
&rr.merchant),
&merchant_pub),
GNUNET_JSON_pack_data_auto ("merchant_sig",
&merchant_sig));
rh = GNUNET_new (struct TALER_EXCHANGE_RefundHandle);
@ -742,7 +733,7 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
rh->depconf.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND);
rh->depconf.h_contract_terms = *h_contract_terms;
rh->depconf.coin_pub = *coin_pub;
rh->depconf.merchant = rr.merchant;
rh->depconf.merchant = merchant_pub;
rh->depconf.rtransaction_id = GNUNET_htonll (rtransaction_id);
TALER_amount_hton (&rh->depconf.refund_amount,
amount);

View File

@ -76,6 +76,7 @@ libtalerutil_la_SOURCES = \
getopt.c \
lang.c \
iban.c \
merchant_signatures.c \
mhd.c \
offline_signatures.c \
payto.c \

View File

@ -0,0 +1,78 @@
/*
This file is part of TALER
Copyright (C) 2020 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 merchant_signatures.c
* @brief Utility functions for Taler merchant signatures
* @author Christian Grothoff
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_signatures.h"
void
TALER_merchant_refund_sign (
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_PrivateContractHash *h_contract_terms,
uint64_t rtransaction_id,
const struct TALER_Amount *amount,
const struct TALER_MerchantPrivateKeyP *merchant_priv,
struct TALER_MerchantSignatureP *merchant_sig)
{
struct TALER_RefundRequestPS rr = {
.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
.purpose.size = htonl (sizeof (rr)),
.h_contract_terms = *h_contract_terms,
.coin_pub = *coin_pub,
.rtransaction_id = GNUNET_htonll (rtransaction_id)
};
TALER_amount_hton (&rr.refund_amount,
amount);
GNUNET_CRYPTO_eddsa_sign (&merchant_priv->eddsa_priv,
&rr,
&merchant_sig->eddsa_sig);
}
enum GNUNET_GenericReturnValue
TALER_merchant_refund_verify (
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_PrivateContractHash *h_contract_terms,
uint64_t rtransaction_id,
const struct TALER_Amount *amount,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_MerchantSignatureP *merchant_sig)
{
struct TALER_RefundRequestPS rr = {
.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
.purpose.size = htonl (sizeof (rr)),
.h_contract_terms = *h_contract_terms,
.coin_pub = *coin_pub,
.rtransaction_id = GNUNET_htonll (rtransaction_id)
};
TALER_amount_hton (&rr.refund_amount,
amount);
return
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
&rr,
&merchant_sig->eddsa_sig,
&merchant_pub->eddsa_pub);
}
/* end of merchant_signatures.c */