diff options
-rw-r--r-- | src/mint-lib/mint_api_refresh.c | 141 | ||||
-rw-r--r-- | src/mint/taler-mint-httpd_responses.c | 2 |
2 files changed, 140 insertions, 3 deletions
diff --git a/src/mint-lib/mint_api_refresh.c b/src/mint-lib/mint_api_refresh.c index dad0ec16b..ede95af5b 100644 --- a/src/mint-lib/mint_api_refresh.c +++ b/src/mint-lib/mint_api_refresh.c @@ -25,6 +25,7 @@ #include <microhttpd.h> /* just for HTTP status codes */ #include <gnunet/gnunet_util_lib.h> #include "taler_mint_service.h" +#include "mint_api_common.h" #include "mint_api_json.h" #include "mint_api_context.h" #include "mint_api_handle.h" @@ -59,6 +60,11 @@ struct MeltedCoinP struct TALER_AmountNBO fee_melt; /** + * The original value of the coin. + */ + struct TALER_AmountNBO original_value; + + /** * Transfer private keys for each cut-and-choose dimension. */ struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA]; @@ -170,6 +176,11 @@ struct MeltedCoin struct TALER_Amount fee_melt; /** + * The original value of the coin. + */ + struct TALER_Amount original_value; + + /** * Transfer private keys for each cut-and-choose dimension. */ struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA]; @@ -381,9 +392,10 @@ serialize_melted_coin (const struct MeltedCoin *mc, mcp.coin_priv = mc->coin_priv; TALER_amount_hton (&mcp.melt_amount_with_fee, &mc->melt_amount_with_fee); - TALER_amount_hton (&mcp.fee_melt, &mc->fee_melt); + TALER_amount_hton (&mcp.original_value, + &mc->original_value); for (i=0;i<TALER_CNC_KAPPA;i++) mcp.transfer_priv[i] = mc->transfer_priv[i]; mcp.deposit_valid_until = GNUNET_TIME_absolute_hton (mc->deposit_valid_until); @@ -464,6 +476,8 @@ deserialize_melted_coin (struct MeltedCoin *mc, &mcp.melt_amount_with_fee); TALER_amount_ntoh (&mc->fee_melt, &mcp.fee_melt); + TALER_amount_ntoh (&mc->original_value, + &mcp.original_value); for (i=0;i<TALER_CNC_KAPPA;i++) mc->transfer_priv[i] = mcp.transfer_priv[i]; mc->deposit_valid_until = GNUNET_TIME_absolute_ntoh (mcp.deposit_valid_until); @@ -868,6 +882,7 @@ TALER_MINT_refresh_prepare (unsigned int num_melts, md.melted_coins[i].coin_priv = melt_privs[i]; md.melted_coins[i].melt_amount_with_fee = melt_amounts[i]; md.melted_coins[i].fee_melt = melt_pks[i].fee_refresh; + md.melted_coins[i].original_value = melt_pks[i].value; for (j=0;j<TALER_CNC_KAPPA;j++) { struct GNUNET_CRYPTO_EcdhePrivateKey *tpk; @@ -1091,6 +1106,122 @@ verify_refresh_melt_signature_ok (struct TALER_MINT_RefreshMeltHandle *rmh, /** + * Verify that the signatures on the "403 FORBIDDEN" response from the + * mint demonstrating customer double-spending are valid. + * + * @param rmh 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_refresh_melt_signature_forbidden (struct TALER_MINT_RefreshMeltHandle *rmh, + json_t *json) +{ + json_t *history; + struct TALER_Amount original_value; + struct TALER_Amount melt_value_with_fee; + struct TALER_Amount total; + struct TALER_CoinSpendPublicKeyP coin_pub; + unsigned int i; + struct MAJ_Specification spec[] = { + // MAJ_spec_json ("history", &history), // FIXME! + MAJ_spec_fixed_auto ("coin_pub", &coin_pub), + MAJ_spec_amount ("original_value", &original_value), + MAJ_spec_amount ("requested_value", &melt_value_with_fee), + MAJ_spec_end + }; + const struct MeltedCoin *mc; + + /* parse JSON reply */ + if (GNUNET_OK != + MAJ_parse_json (json, + spec)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + /* Find out which coin was deemed problematic by the mint */ + mc = NULL; + for (i=0;i<rmh->md->num_melted_coins;i++) + { + if (0 == TALER_amount_cmp (&melt_value_with_fee, + &rmh->md->melted_coins[i].melt_amount_with_fee)) + { + struct TALER_CoinSpendPublicKeyP mc_pub; + + GNUNET_CRYPTO_eddsa_key_get_public (&rmh->md->melted_coins[i].coin_priv.eddsa_priv, + &mc_pub.eddsa_pub); + if (0 == memcmp (&mc_pub, + &coin_pub, + sizeof (struct TALER_CoinSpendPublicKeyP))) + { + mc = &rmh->md->melted_coins[i]; + break; + } + } + } + if (NULL == mc) + { + /* coin not found in our original request */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + /* check basic coin properties */ + if (0 != TALER_amount_cmp (&original_value, + &mc->original_value)) + { + /* We disagree on the value of the coin */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (0 != TALER_amount_cmp (&melt_value_with_fee, + &mc->melt_amount_with_fee)) + { + /* We disagree on the value of the coin */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + /* verify coin history */ + history = json_object_get (json, + "history"); + if (GNUNET_OK != + TALER_MINT_verify_coin_history_ (original_value.currency, + &coin_pub, + history, + &total)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + /* check if melt operation was really too expensive given history */ + if (GNUNET_OK != + TALER_amount_add (&total, + &total, + &melt_value_with_fee)) + { + /* clearly not OK if our transaction would have caused + the overflow... */ + return GNUNET_OK; + } + + if (0 >= TALER_amount_cmp (&total, + &original_value)) + { + /* transaction should have still fit */ + GNUNET_break (0); + return GNUNET_SYSERR; + } + + /* everything OK, valid proof of double-spending was provided */ + return GNUNET_OK; +} + + +/** * Function called when we're done processing the * HTTP /refresh/melt request. * @@ -1138,7 +1269,13 @@ handle_refresh_melt_finished (void *cls, break; case MHD_HTTP_FORBIDDEN: /* Double spending; check signatures on transaction history */ - GNUNET_break (0); // FIXME: NOT implemented! + if (GNUNET_OK != + verify_refresh_melt_signature_forbidden (rmh, + json)) + { + GNUNET_break_op (0); + response_code = 0; + } break; case MHD_HTTP_UNAUTHORIZED: /* Nothing really to verify, mint says one of the signatures is diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index 48c0a748d..0e2f90709 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -695,7 +695,7 @@ TMH_RESPONSE_reply_withdraw_sign_success (struct MHD_Connection *connection, * @param coin_pub public key of the coin * @param coin_value original value of the coin * @param tl transaction history for the coin - * @param requested how much this coin was supposed to contribute + * @param requested how much this coin was supposed to contribute, including fee * @param residual remaining value of the coin (after subtracting @a tl) * @return a MHD result code */ |