test coin_priv re-use with deposit and refresh, update handling of the error code client-side
This commit is contained in:
parent
ddf95c491a
commit
7085cfef70
@ -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.
|
||||
|
@ -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,7 +209,7 @@ 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,
|
||||
verify_melt_signature_spend_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
|
||||
const json_t *json)
|
||||
{
|
||||
json_t *history;
|
||||
@ -329,9 +376,13 @@ handle_melt_finished (void *cls,
|
||||
hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
case MHD_HTTP_CONFLICT:
|
||||
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_conflict (mh,
|
||||
verify_melt_signature_spend_conflict (mh,
|
||||
j))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
@ -340,6 +391,25 @@ handle_melt_finished (void *cls,
|
||||
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:
|
||||
/* Nothing really to verify, exchange says one of the signatures is
|
||||
invalid; assuming we checked them, this should never happen, we
|
||||
@ -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 */
|
||||
|
@ -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,12 +194,13 @@ 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",
|
||||
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"),
|
||||
|
@ -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;
|
||||
}
|
||||
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.
|
||||
|
Loading…
Reference in New Issue
Block a user