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);
|
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
|
* Create withdraw command, letting the caller specify the
|
||||||
* amount by a denomination key.
|
* amount by a denomination key.
|
||||||
|
@ -75,6 +75,11 @@ struct TALER_EXCHANGE_MeltHandle
|
|||||||
*/
|
*/
|
||||||
struct MeltData *md;
|
struct MeltData *md;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public key of the coin being melted.
|
||||||
|
*/
|
||||||
|
struct TALER_CoinSpendPublicKeyP coin_pub;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Public information about the coin's denomination key
|
* @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
|
* Verify that the signatures on the "409 CONFLICT" response from the
|
||||||
* exchange demonstrating customer double-spending are valid.
|
* 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
|
* @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
|
||||||
*/
|
*/
|
||||||
static int
|
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)
|
const json_t *json)
|
||||||
{
|
{
|
||||||
json_t *history;
|
json_t *history;
|
||||||
@ -329,9 +376,13 @@ handle_melt_finished (void *cls,
|
|||||||
hr.hint = TALER_JSON_get_error_hint (j);
|
hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_CONFLICT:
|
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 */
|
/* Double spending; check signatures on transaction history */
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
verify_melt_signature_conflict (mh,
|
verify_melt_signature_spend_conflict (mh,
|
||||||
j))
|
j))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
@ -340,6 +391,25 @@ handle_melt_finished (void *cls,
|
|||||||
hr.hint = TALER_JSON_get_error_hint (j);
|
hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case MHD_HTTP_FORBIDDEN:
|
||||||
/* Nothing really to verify, exchange says one of the signatures is
|
/* Nothing really to verify, exchange says one of the signatures is
|
||||||
invalid; assuming we checked them, this should never happen, we
|
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 */
|
/* and now we can at last begin the actual request handling */
|
||||||
mh = GNUNET_new (struct TALER_EXCHANGE_MeltHandle);
|
mh = GNUNET_new (struct TALER_EXCHANGE_MeltHandle);
|
||||||
mh->exchange = exchange;
|
mh->exchange = exchange;
|
||||||
|
mh->coin_pub = melt.coin_pub;
|
||||||
mh->dki = *dki;
|
mh->dki = *dki;
|
||||||
mh->dki.key.rsa_public_key = NULL; /* lifetime not warranted, so better
|
mh->dki.key.rsa_public_key = NULL; /* lifetime not warranted, so better
|
||||||
not copy the pointer */
|
not copy the pointer */
|
||||||
|
@ -136,12 +136,12 @@ run (void *cls,
|
|||||||
* Do another transfer to the same reserve
|
* Do another transfer to the same reserve
|
||||||
*/
|
*/
|
||||||
TALER_TESTING_cmd_admin_add_incoming_with_ref ("create-reserve-1.2",
|
TALER_TESTING_cmd_admin_add_incoming_with_ref ("create-reserve-1.2",
|
||||||
"EUR:1",
|
"EUR:2.01",
|
||||||
&bc.exchange_auth,
|
&bc.exchange_auth,
|
||||||
bc.user42_payto,
|
bc.user42_payto,
|
||||||
"create-reserve-1"),
|
"create-reserve-1"),
|
||||||
TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-1.2",
|
TALER_TESTING_cmd_check_bank_admin_transfer ("check-create-reserve-1.2",
|
||||||
"EUR:1",
|
"EUR:2.01",
|
||||||
bc.user42_payto,
|
bc.user42_payto,
|
||||||
bc.exchange_payto,
|
bc.exchange_payto,
|
||||||
"create-reserve-1.2"),
|
"create-reserve-1.2"),
|
||||||
@ -153,6 +153,15 @@ run (void *cls,
|
|||||||
"create-reserve-1",
|
"create-reserve-1",
|
||||||
"EUR:5",
|
"EUR:5",
|
||||||
MHD_HTTP_OK),
|
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.
|
* Check the reserve is depleted.
|
||||||
*/
|
*/
|
||||||
@ -160,6 +169,13 @@ run (void *cls,
|
|||||||
"create-reserve-1",
|
"create-reserve-1",
|
||||||
"EUR:0",
|
"EUR:0",
|
||||||
MHD_HTTP_OK),
|
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 ()
|
TALER_TESTING_cmd_end ()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -178,12 +194,13 @@ run (void *cls,
|
|||||||
TALER_TESTING_cmd_deposit_replay ("deposit-simple-replay",
|
TALER_TESTING_cmd_deposit_replay ("deposit-simple-replay",
|
||||||
"deposit-simple",
|
"deposit-simple",
|
||||||
MHD_HTTP_OK),
|
MHD_HTTP_OK),
|
||||||
/*
|
TALER_TESTING_cmd_deposit ("deposit-reused-coin-key-failure",
|
||||||
* Try to overdraw.
|
"withdraw-coin-1x",
|
||||||
*/
|
0,
|
||||||
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-2",
|
bc.user42_payto,
|
||||||
"create-reserve-1",
|
"{\"items\":[{\"name\":\"ice cream\",\"value\":1}]}",
|
||||||
"EUR:5",
|
GNUNET_TIME_UNIT_ZERO,
|
||||||
|
"EUR:1",
|
||||||
MHD_HTTP_CONFLICT),
|
MHD_HTTP_CONFLICT),
|
||||||
/**
|
/**
|
||||||
* Try to double spend using different wire details.
|
* Try to double spend using different wire details.
|
||||||
@ -225,6 +242,14 @@ run (void *cls,
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct TALER_TESTING_Command refresh[] = {
|
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. */
|
/* Fill reserve with EUR:5, 1ct is for fees. */
|
||||||
CMD_TRANSFER_TO_EXCHANGE ("refresh-create-reserve-1",
|
CMD_TRANSFER_TO_EXCHANGE ("refresh-create-reserve-1",
|
||||||
"EUR:5.01"),
|
"EUR:5.01"),
|
||||||
|
@ -59,6 +59,12 @@ struct WithdrawState
|
|||||||
*/
|
*/
|
||||||
const char *reserve_reference;
|
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.
|
* String describing the denomination value we should withdraw.
|
||||||
* A corresponding denomination key must exist in the exchange's
|
* 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.
|
* Run the command.
|
||||||
*/
|
*/
|
||||||
@ -307,7 +357,32 @@ withdraw_run (void *cls,
|
|||||||
TALER_TESTING_interpreter_fail (is);
|
TALER_TESTING_interpreter_fail (is);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (NULL == ws->reuse_coin_key_ref)
|
||||||
|
{
|
||||||
TALER_planchet_setup_random (&ws->ps);
|
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;
|
ws->is = is;
|
||||||
if (NULL == ws->pk)
|
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
|
* Create withdraw command, letting the caller specify the
|
||||||
* amount by a denomination key.
|
* amount by a denomination key.
|
||||||
|
Loading…
Reference in New Issue
Block a user