[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-exchange] branch master updated: test coin_priv re-use with depos
From: |
gnunet |
Subject: |
[taler-exchange] branch master updated: test coin_priv re-use with deposit and refresh, update handling of the error code client-side |
Date: |
Fri, 10 Jul 2020 23:09:48 +0200 |
This is an automated email from the git hooks/post-receive script.
grothoff pushed a commit to branch master
in repository exchange.
The following commit(s) were added to refs/heads/master by this push:
new 7085cfef test coin_priv re-use with deposit and refresh, update
handling of the error code client-side
7085cfef is described below
commit 7085cfef70889a11508cdac4cd887b9959f59218
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Fri Jul 10 23:09:46 2020 +0200
test coin_priv re-use with deposit and refresh, update handling of the
error code client-side
---
src/include/taler_testing_lib.h | 24 +++++++
src/lib/exchange_api_melt.c | 83 ++++++++++++++++++++++--
src/testing/test_exchange_api.c | 43 +++++++++---
src/testing/testing_api_cmd_withdraw.c | 115 ++++++++++++++++++++++++++++++++-
4 files changed, 249 insertions(+), 16 deletions(-)
diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
index c3278b55..332b429f 100644
--- a/src/include/taler_testing_lib.h
+++ b/src/include/taler_testing_lib.h
@@ -1295,6 +1295,30 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
unsigned int expected_response_code);
+/**
+ * Create a withdraw command, letting the caller specify
+ * the desired amount as string and also re-using an existing
+ * coin private key in the process (violating the specification,
+ * which will result in an error when spending the coin!).
+ *
+ * @param label command label.
+ * @param reserve_reference command providing us with a reserve to withdraw
from
+ * @param amount how much we withdraw.
+ * @param coin_ref reference to (withdraw/reveal) command of a coin
+ * from which we should re-use the private key
+ * @param expected_response_code which HTTP response code
+ * we expect from the exchange.
+ * @return the withdraw command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_withdraw_amount_reuse_key (
+ const char *label,
+ const char *reserve_reference,
+ const char *amount,
+ const char *coin_ref,
+ unsigned int expected_response_code);
+
+
/**
* Create withdraw command, letting the caller specify the
* amount by a denomination key.
diff --git a/src/lib/exchange_api_melt.c b/src/lib/exchange_api_melt.c
index 2c1e85d7..989c6501 100644
--- a/src/lib/exchange_api_melt.c
+++ b/src/lib/exchange_api_melt.c
@@ -75,6 +75,11 @@ struct TALER_EXCHANGE_MeltHandle
*/
struct MeltData *md;
+ /**
+ * Public key of the coin being melted.
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
/**
* @brief Public information about the coin's denomination key
*/
@@ -153,6 +158,48 @@ verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle
*mh,
}
+/**
+ * Verify that the signatures on the "409 CONFLICT" response from the
+ * exchange demonstrating customer denomination key differences
+ * resulting from coin private key reuse are valid.
+ *
+ * @param mh melt handle
+ * @param json json reply with the signature(s) and transaction history
+ * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
+ */
+static int
+verify_melt_signature_denom_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
+ const json_t *json)
+
+{
+ json_t *history;
+ struct TALER_Amount total;
+ struct GNUNET_HashCode h_denom_pub;
+
+ memset (&h_denom_pub,
+ 0,
+ sizeof (h_denom_pub));
+ history = json_object_get (json,
+ "history");
+ if (GNUNET_OK !=
+ TALER_EXCHANGE_verify_coin_history (&mh->dki,
+ mh->dki.value.currency,
+ &mh->coin_pub,
+ history,
+ &h_denom_pub,
+ &total))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (0 != GNUNET_memcmp (&mh->dki.h_key,
+ &h_denom_pub))
+ return GNUNET_OK; /* indeed, proof with different denomination key
provided */
+ /* invalid proof provided */
+ return GNUNET_SYSERR;
+}
+
+
/**
* Verify that the signatures on the "409 CONFLICT" response from the
* exchange demonstrating customer double-spending are valid.
@@ -162,8 +209,8 @@ verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle
*mh,
* @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
*/
static int
-verify_melt_signature_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
- const json_t *json)
+verify_melt_signature_spend_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
+ const json_t *json)
{
json_t *history;
struct TALER_Amount original_value;
@@ -329,15 +376,38 @@ handle_melt_finished (void *cls,
hr.hint = TALER_JSON_get_error_hint (j);
break;
case MHD_HTTP_CONFLICT:
- /* Double spending; check signatures on transaction history */
- if (GNUNET_OK !=
- verify_melt_signature_conflict (mh,
- j))
+ hr.ec = TALER_JSON_get_error_code (j);
+ switch (hr.ec)
{
+ case TALER_EC_MELT_INSUFFICIENT_FUNDS:
+ /* Double spending; check signatures on transaction history */
+ if (GNUNET_OK !=
+ verify_melt_signature_spend_conflict (mh,
+ j))
+ {
+ GNUNET_break_op (0);
+ hr.http_status = 0;
+ hr.ec = TALER_EC_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
+ hr.hint = TALER_JSON_get_error_hint (j);
+ }
+ break;
+ case TALER_EC_COIN_CONFLICTING_DENOMINATION_KEY:
+ if (GNUNET_OK !=
+ verify_melt_signature_denom_conflict (mh,
+ j))
+ {
+ GNUNET_break_op (0);
+ hr.http_status = 0;
+ hr.ec = TALER_EC_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
+ hr.hint = TALER_JSON_get_error_hint (j);
+ }
+ break;
+ default:
GNUNET_break_op (0);
hr.http_status = 0;
hr.ec = TALER_EC_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
hr.hint = TALER_JSON_get_error_hint (j);
+ break;
}
break;
case MHD_HTTP_FORBIDDEN:
@@ -485,6 +555,7 @@ TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange,
/* and now we can at last begin the actual request handling */
mh = GNUNET_new (struct TALER_EXCHANGE_MeltHandle);
mh->exchange = exchange;
+ mh->coin_pub = melt.coin_pub;
mh->dki = *dki;
mh->dki.key.rsa_public_key = NULL; /* lifetime not warranted, so better
not copy the pointer */
diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c
index 410c1a49..75e3d480 100644
--- a/src/testing/test_exchange_api.c
+++ b/src/testing/test_exchange_api.c
@@ -136,12 +136,12 @@ run (void *cls,
* Do another transfer to the same reserve
*/
TALER_TESTING_cmd_admin_add_incoming_with_ref ("create-reserve-1.2",
- "EUR:1",
+ "EUR:2.01",
&bc.exchange_auth,
bc.user42_payto,
"create-reserve-1"),
TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-1.2",
- "EUR:1",
+ "EUR:2.01",
bc.user42_payto,
bc.exchange_payto,
"create-reserve-1.2"),
@@ -153,6 +153,15 @@ run (void *cls,
"create-reserve-1",
"EUR:5",
MHD_HTTP_OK),
+ /**
+ * Withdraw EUR:1 using the SAME private coin key as for the previous coin
+ * (in violation of the specification, to be detected on spending!).
+ */
+ TALER_TESTING_cmd_withdraw_amount_reuse_key ("withdraw-coin-1x",
+ "create-reserve-1",
+ "EUR:1",
+ "withdraw-coin-1",
+ MHD_HTTP_OK),
/**
* Check the reserve is depleted.
*/
@@ -160,6 +169,13 @@ run (void *cls,
"create-reserve-1",
"EUR:0",
MHD_HTTP_OK),
+ /*
+ * Try to overdraw.
+ */
+ TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2",
+ "create-reserve-1",
+ "EUR:5",
+ MHD_HTTP_CONFLICT),
TALER_TESTING_cmd_end ()
};
@@ -178,13 +194,14 @@ run (void *cls,
TALER_TESTING_cmd_deposit_replay ("deposit-simple-replay",
"deposit-simple",
MHD_HTTP_OK),
- /*
- * Try to overdraw.
- */
- TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2",
- "create-reserve-1",
- "EUR:5",
- MHD_HTTP_CONFLICT),
+ TALER_TESTING_cmd_deposit ("deposit-reused-coin-key-failure",
+ "withdraw-coin-1x",
+ 0,
+ bc.user42_payto,
+ "{\"items\":[{\"name\":\"ice
cream\",\"value\":1}]}",
+ GNUNET_TIME_UNIT_ZERO,
+ "EUR:1",
+ MHD_HTTP_CONFLICT),
/**
* Try to double spend using different wire details.
*/
@@ -225,6 +242,14 @@ run (void *cls,
};
struct TALER_TESTING_Command refresh[] = {
+ /**
+ * Try to melt the coin that shared the private key with another
+ * coin (should fail). */
+ TALER_TESTING_cmd_melt ("refresh-melt-reused-coin-key-failure",
+ "withdraw-coin-1x",
+ MHD_HTTP_CONFLICT,
+ NULL),
+
/* Fill reserve with EUR:5, 1ct is for fees. */
CMD_TRANSFER_TO_EXCHANGE ("refresh-create-reserve-1",
"EUR:5.01"),
diff --git a/src/testing/testing_api_cmd_withdraw.c
b/src/testing/testing_api_cmd_withdraw.c
index 5db97cbf..5b2ad26e 100644
--- a/src/testing/testing_api_cmd_withdraw.c
+++ b/src/testing/testing_api_cmd_withdraw.c
@@ -59,6 +59,12 @@ struct WithdrawState
*/
const char *reserve_reference;
+ /**
+ * Reference to a withdraw or reveal operation from which we should
+ * re-use the private coin key, or NULL for regular withdrawal.
+ */
+ const char *reuse_coin_key_ref;
+
/**
* String describing the denomination value we should withdraw.
* A corresponding denomination key must exist in the exchange's
@@ -274,6 +280,50 @@ reserve_withdraw_cb (void *cls,
}
+/**
+ * Parser reference to a coin.
+ *
+ * @param coin_reference of format $LABEL['#' $INDEX]?
+ * @param[out] cref where we return a copy of $LABEL
+ * @param[out] idx where we set $INDEX
+ * @return #GNUNET_SYSERR if $INDEX is present but not numeric
+ */
+static int
+parse_coin_reference (const char *coin_reference,
+ char **cref,
+ unsigned int *idx)
+{
+ const char *index;
+
+ /* We allow command references of the form "$LABEL#$INDEX" or
+ just "$LABEL", which implies the index is 0. Figure out
+ which one it is. */
+ index = strchr (coin_reference, '#');
+ if (NULL == index)
+ {
+ *idx = 0;
+ *cref = GNUNET_strdup (coin_reference);
+ return GNUNET_OK;
+ }
+ *cref = GNUNET_strndup (coin_reference,
+ index - coin_reference);
+ if (1 != sscanf (index + 1,
+ "%u",
+ idx))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Numeric index (not `%s') required after `#' in command
reference of command in %s:%u\n",
+ index,
+ __FILE__,
+ __LINE__);
+ GNUNET_free (*cref);
+ *cref = NULL;
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
/**
* Run the command.
*/
@@ -307,7 +357,32 @@ withdraw_run (void *cls,
TALER_TESTING_interpreter_fail (is);
return;
}
- TALER_planchet_setup_random (&ws->ps);
+ if (NULL == ws->reuse_coin_key_ref)
+ {
+ TALER_planchet_setup_random (&ws->ps);
+ }
+ else
+ {
+ const struct TALER_CoinSpendPrivateKeyP *coin_priv;
+ const struct TALER_TESTING_Command *cref;
+ char *cstr;
+ unsigned int index;
+
+ GNUNET_assert (GNUNET_OK ==
+ parse_coin_reference (ws->reuse_coin_key_ref,
+ &cstr,
+ &index));
+ cref = TALER_TESTING_interpreter_lookup_command (is,
+ cstr);
+ GNUNET_assert (NULL != cref);
+ GNUNET_free (cstr);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_TESTING_get_trait_coin_priv (cref,
+ index,
+ &coin_priv));
+ TALER_planchet_setup_random (&ws->ps);
+ ws->ps.coin_priv = *coin_priv;
+ }
ws->is = is;
if (NULL == ws->pk)
{
@@ -526,6 +601,44 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
}
+/**
+ * Create a withdraw command, letting the caller specify
+ * the desired amount as string and also re-using an existing
+ * coin private key in the process (violating the specification,
+ * which will result in an error when spending the coin!).
+ *
+ * @param label command label.
+ * @param reserve_reference command providing us with a reserve to withdraw
from
+ * @param amount how much we withdraw.
+ * @param coin_ref reference to (withdraw/reveal) command of a coin
+ * from which we should re-use the private key
+ * @param expected_response_code which HTTP response code
+ * we expect from the exchange.
+ * @return the withdraw command to be executed by the interpreter.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_withdraw_amount_reuse_key (
+ const char *label,
+ const char *reserve_reference,
+ const char *amount,
+ const char *coin_ref,
+ unsigned int expected_response_code)
+{
+ struct TALER_TESTING_Command cmd;
+
+ cmd = TALER_TESTING_cmd_withdraw_amount (label,
+ reserve_reference,
+ amount,
+ expected_response_code);
+ {
+ struct WithdrawState *ws = cmd.cls;
+
+ ws->reuse_coin_key_ref = coin_ref;
+ }
+ return cmd;
+}
+
+
/**
* Create withdraw command, letting the caller specify the
* amount by a denomination key.
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-exchange] branch master updated: test coin_priv re-use with deposit and refresh, update handling of the error code client-side,
gnunet <=