implementing #3632: generate proof of insufficient funds by converting transaction history to JSON

This commit is contained in:
Christian Grothoff 2015-03-09 12:29:41 +01:00
parent 7b0ae9c1d0
commit 579f465c9b
8 changed files with 228 additions and 72 deletions

View File

@ -60,8 +60,20 @@ TALER_JSON_from_abs (struct GNUNET_TIME_Absolute stamp);
* @return the JSON reporesentation of the signature with purpose * @return the JSON reporesentation of the signature with purpose
*/ */
json_t * json_t *
TALER_JSON_from_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, TALER_JSON_from_eddsa_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
const struct GNUNET_CRYPTO_EddsaSignature *signature); const struct GNUNET_CRYPTO_EddsaSignature *signature);
/**
* Convert a signature (with purpose) to a JSON object representation.
*
* @param purpose purpose of the signature
* @param signature the signature
* @return the JSON reporesentation of the signature with purpose
*/
json_t *
TALER_JSON_from_ecdsa_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
const struct GNUNET_CRYPTO_EcdsaSignature *signature);
/** /**
@ -76,6 +88,17 @@ json_t *
TALER_JSON_from_data (const void *data, size_t size); TALER_JSON_from_data (const void *data, size_t size);
/**
* Convert binary hash to a JSON string with the base32crockford
* encoding.
*
* @param hc binary data
* @return json string that encodes @a hc
*/
json_t *
TALER_JSON_from_hash (const struct GNUNET_HashCode *hc);
/** /**
* Parse given JSON object to Amount * Parse given JSON object to Amount
* *
@ -119,7 +142,8 @@ TALER_JSON_to_data (json_t *json,
* @return 1 if correctly formatted; 0 if not * @return 1 if correctly formatted; 0 if not
*/ */
int int
TALER_JSON_validate_wireformat (const char *type, json_t *wire); TALER_JSON_validate_wireformat (const char *type,
json_t *wire);
#endif /* TALER_JSON_LIB_H_ */ #endif /* TALER_JSON_LIB_H_ */

View File

@ -266,7 +266,83 @@ struct RefreshMeltResponseSignatureBody
}; };
/**
* Message signed by a coin to indicate that the coin should
* be melted.
*/
struct RefreshMeltSignatureBody
{
/**
* Purpose is #TALER_SIGNATURE_REFRESH_MELT.
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
* Which melting operation should the coin become a part of.
*/
struct GNUNET_HashCode melt_hash;
/**
* How much of the value of the coin should be melted?
* This amount includes the fees, so the final amount contributed
* to the melt is this value minus the fee for melting the coin.
*/
struct TALER_AmountNBO amount;
};
/**
* Message signed during melting committing the client to the
* hashed inputs.
*/
struct RefreshCommitSignatureBody
{
/**
* Purpose is #TALER_SIGNATURE_REFRESH_COMMIT.
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
* Session state the client commits itself to.
*/
struct GNUNET_HashCode commit_hash;
};
/**
* Message signed by the mint, committing it to a particular
* index to not be revealed during the refresh.
*/
struct RefreshCommitResponseSignatureBody
{
/**
* Purpose is #TALER_SIGNATURE_REFRESH_MELT_RESPONSE.
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
* Index that the client will not have to reveal.
*/
uint16_t noreveal_index GNUNET_PACKED;
};
/**
* Message signed by the client requesting the final
* result of the melting operation.
*/
struct RefreshMeltConfirmSignRequestBody
{
/**
* Purpose is #TALER_SIGNATURE_REFRESH_MELT_CONFIRM.
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
* FIXME.
*/
struct GNUNET_CRYPTO_EddsaPublicKey session_pub;
};
/** /**
@ -303,45 +379,6 @@ struct TALER_MINT_DenomKeyIssue
}; };
/**
* FIXME
*/
struct RefreshMeltSignatureBody
{
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
struct GNUNET_HashCode melt_hash;
};
/**
* FIXME
*/
struct RefreshCommitSignatureBody
{
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
struct GNUNET_HashCode commit_hash;
};
/**
* FIXME
*/
struct RefreshCommitResponseSignatureBody
{
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
uint16_t noreveal_index;
};
/**
* FIXME
*/
struct RefreshMeltConfirmSignRequestBody
{
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
struct GNUNET_CRYPTO_EddsaPublicKey session_pub;
};
GNUNET_NETWORK_STRUCT_END GNUNET_NETWORK_STRUCT_END

View File

@ -222,8 +222,6 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db,
const struct TALER_Amount balance, const struct TALER_Amount balance,
const struct GNUNET_TIME_Absolute expiry); const struct GNUNET_TIME_Absolute expiry);
/* FIXME: need call to convert CollectableBlindcoin to JSON (#3527) */
/** /**
* Locate the response for a /withdraw request under the * Locate the response for a /withdraw request under the
@ -529,10 +527,8 @@ TALER_MINT_DB_update_refresh_session (PGconn *db_conn,
/** /**
* Specification for coin in a /refresh/melt operation. * Specification for coin in a /refresh/melt operation.
* FIXME: same as `struct MeltDetails`, and not by accident!
* We should merge the structs!
*/ */
struct RefreshMelt /* FIXME: name to make it clearer this is about ONE coin! */ struct RefreshMelt
{ {
/** /**
* Information about the coin that is being melted. * Information about the coin that is being melted.
@ -544,8 +540,15 @@ struct RefreshMelt /* FIXME: name to make it clearer this is about ONE coin! */
*/ */
struct GNUNET_CRYPTO_EcdsaSignature coin_sig; struct GNUNET_CRYPTO_EcdsaSignature coin_sig;
/**
* Which melting operation should the coin become a part of.
*/
struct GNUNET_HashCode melt_hash;
/** /**
* How much value is being melted? * How much value is being melted?
* This amount includes the fees, so the final amount contributed
* to the melt is this value minus the fee for melting the coin.
*/ */
struct TALER_Amount amount; struct TALER_Amount amount;

View File

@ -22,10 +22,6 @@
* - actually abstract DB implementation (i.e. via plugin logic) * - actually abstract DB implementation (i.e. via plugin logic)
* (this file should remain largely unchanged with the exception * (this file should remain largely unchanged with the exception
* of the PQ-specific DB handle types) * of the PQ-specific DB handle types)
* - /refresh/link:
* + check low-level API
* + separate DB logic from response generation
* + check for leaks
*/ */
#include "platform.h" #include "platform.h"
#include <pthread.h> #include <pthread.h>
@ -354,8 +350,8 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
break; break;
} }
} }
GNUNET_break (0 > TALER_amount_cmp (withdraw_total,
/* FIXME: good place to assert deposit_total > withdraw_total... */ deposit_total));
balance = TALER_amount_subtract (deposit_total, balance = TALER_amount_subtract (deposit_total,
withdraw_total); withdraw_total);
if (0 < TALER_amount_cmp (amount_required, if (0 < TALER_amount_cmp (amount_required,
@ -382,8 +378,6 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection,
return TALER_MINT_reply_internal_error (connection, return TALER_MINT_reply_internal_error (connection,
"Internal error"); "Internal error");
} }
// FIXME: can we avoid the cast?
collectable.denom_pub = (struct GNUNET_CRYPTO_rsa_PublicKey *) denomination_pub; collectable.denom_pub = (struct GNUNET_CRYPTO_rsa_PublicKey *) denomination_pub;
collectable.sig = sig; collectable.sig = sig;
collectable.reserve_pub = *reserve; collectable.reserve_pub = *reserve;
@ -430,6 +424,7 @@ static int
refresh_accept_melts (struct MHD_Connection *connection, refresh_accept_melts (struct MHD_Connection *connection,
PGconn *db_conn, PGconn *db_conn,
const struct MintKeyState *key_state, const struct MintKeyState *key_state,
const struct GNUNET_HashCode *melt_hash,
const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub,
const struct TALER_CoinPublicInfo *coin_public_info, const struct TALER_CoinPublicInfo *coin_public_info,
const struct MeltDetails *coin_details, const struct MeltDetails *coin_details,
@ -474,6 +469,7 @@ refresh_accept_melts (struct MHD_Connection *connection,
melt.coin = *coin_public_info; melt.coin = *coin_public_info;
melt.coin_sig = coin_details->melt_sig; melt.coin_sig = coin_details->melt_sig;
melt.melt_hash = *melt_hash;
melt.amount = coin_details->melt_amount; melt.amount = coin_details->melt_amount;
if (GNUNET_OK != if (GNUNET_OK !=
TALER_MINT_DB_insert_refresh_melt (db_conn, TALER_MINT_DB_insert_refresh_melt (db_conn,
@ -496,6 +492,7 @@ refresh_accept_melts (struct MHD_Connection *connection,
* melted and confirm the melting operation to the client. * melted and confirm the melting operation to the client.
* *
* @param connection the MHD connection to handle * @param connection the MHD connection to handle
* @param melt_hash hash code of the session the coins are melted into
* @param refresh_session_pub public key of the refresh session * @param refresh_session_pub public key of the refresh session
* @param client_signature signature of the client (matching @a refresh_session_pub) * @param client_signature signature of the client (matching @a refresh_session_pub)
* over the melting request * over the melting request
@ -508,6 +505,7 @@ refresh_accept_melts (struct MHD_Connection *connection,
*/ */
int int
TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,
const struct GNUNET_HashCode *melt_hash,
const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
const struct GNUNET_CRYPTO_EddsaSignature *client_signature, const struct GNUNET_CRYPTO_EddsaSignature *client_signature,
unsigned int num_new_denoms, unsigned int num_new_denoms,
@ -558,6 +556,7 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,
(res = refresh_accept_melts (connection, (res = refresh_accept_melts (connection,
db_conn, db_conn,
key_state, key_state,
melt_hash,
refresh_session_pub, refresh_session_pub,
&coin_public_infos[i], &coin_public_infos[i],
&coin_melt_details[i], &coin_melt_details[i],

View File

@ -93,7 +93,8 @@ struct MeltDetails
/** /**
* How much of the coin's value did the client allow to be melted? * How much of the coin's value did the client allow to be melted?
* (FIXME: are the fees included here!?) * This amount includes the fees, so the final amount contributed
* to the melt is this value minus the fee for melting the coin.
*/ */
struct TALER_Amount melt_amount; struct TALER_Amount melt_amount;
}; };
@ -107,6 +108,7 @@ struct MeltDetails
* melted and confirm the melting operation to the client. * melted and confirm the melting operation to the client.
* *
* @param connection the MHD connection to handle * @param connection the MHD connection to handle
* @param melt_hash hash code of the session the coins are melted into
* @param refresh_session_pub public key of the refresh session * @param refresh_session_pub public key of the refresh session
* @param client_signature signature of the client (matching @a refresh_session_pub) * @param client_signature signature of the client (matching @a refresh_session_pub)
* over the melting request * over the melting request
@ -119,6 +121,7 @@ struct MeltDetails
*/ */
int int
TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection,
const struct GNUNET_HashCode *melt_hash,
const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
const struct GNUNET_CRYPTO_EddsaSignature *client_signature, const struct GNUNET_CRYPTO_EddsaSignature *client_signature,
unsigned int num_new_denoms, unsigned int num_new_denoms,

View File

@ -195,9 +195,10 @@ handle_refresh_melt_binary (struct MHD_Connection *connection,
GNUNET_CRYPTO_hash_context_finish (hash_context, GNUNET_CRYPTO_hash_context_finish (hash_context,
&melt_hash); &melt_hash);
body.melt_hash = melt_hash;
body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT); body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT);
body.purpose.size = htonl (sizeof (struct RefreshMeltSignatureBody)); body.purpose.size = htonl (sizeof (struct RefreshMeltSignatureBody));
body.melt_hash = melt_hash;
body.amount = TALER_amount_hton (coin_melt_details->melt_amount);
if (GNUNET_OK != if (GNUNET_OK !=
(res = request_json_check_signature (connection, (res = request_json_check_signature (connection,
@ -247,6 +248,7 @@ handle_refresh_melt_binary (struct MHD_Connection *connection,
/* FIXME: we must also store the signature over the melt! (#3635) */ /* FIXME: we must also store the signature over the melt! (#3635) */
return TALER_MINT_db_execute_refresh_melt (connection, return TALER_MINT_db_execute_refresh_melt (connection,
&melt_hash,
refresh_session_pub, refresh_session_pub,
NULL, /* FIXME: #3635! */ NULL, /* FIXME: #3635! */
num_new_denoms, num_new_denoms,

View File

@ -305,7 +305,7 @@ TALER_MINT_reply_deposit_success (struct MHD_Connection *connection,
dc.merchant = *merchant; dc.merchant = *merchant;
TALER_MINT_keys_sign (&dc.purpose, TALER_MINT_keys_sign (&dc.purpose,
&sig); &sig);
sig_json = TALER_JSON_from_sig (&dc.purpose, &sig); sig_json = TALER_JSON_from_eddsa_sig (&dc.purpose, &sig);
ret = TALER_MINT_reply_json_pack (connection, ret = TALER_MINT_reply_json_pack (connection,
MHD_HTTP_OK, MHD_HTTP_OK,
"{s:s, s:o}", "{s:s, s:o}",
@ -332,28 +332,72 @@ TALER_MINT_reply_insufficient_funds (struct MHD_Connection *connection,
{ {
const struct TALER_MINT_DB_TransactionList *pos; const struct TALER_MINT_DB_TransactionList *pos;
int ret; int ret;
json_t *history;
json_t *transaction;
const char *type;
struct TALER_Amount value;
// FIXME: implement properly! (#3632) history = json_array ();
for (pos = tl; NULL != pos; pos = pos->next) for (pos = tl; NULL != pos; pos = pos->next)
{ {
switch (pos->type) switch (pos->type)
{ {
case TALER_MINT_DB_TT_DEPOSIT: case TALER_MINT_DB_TT_DEPOSIT:
/* FIXME: add operation details to json reply */ {
break; struct TALER_DepositRequest dr;
const struct Deposit *deposit = pos->details.deposit;
type = "deposit";
value = deposit->amount;
dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_DEPOSIT);
dr.purpose.size = htonl (sizeof (struct TALER_DepositRequest));
dr.h_contract = deposit->h_contract;
dr.h_wire = deposit->h_wire;
dr.transaction_id = GNUNET_htonll (deposit->transaction_id);
dr.amount = TALER_amount_hton (deposit->amount);
dr.coin_pub = deposit->coin.coin_pub;
transaction = TALER_JSON_from_ecdsa_sig (&dr.purpose,
&deposit->csig);
break;
}
case TALER_MINT_DB_TT_REFRESH_MELT: case TALER_MINT_DB_TT_REFRESH_MELT:
/* FIXME: add operation details to json reply */ {
struct RefreshMeltSignatureBody ms;
const struct RefreshMelt *melt = pos->details.melt;
type = "melt";
value = melt->amount;
ms.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT);
ms.purpose.size = htonl (sizeof (struct RefreshMeltSignatureBody));
ms.melt_hash = melt->melt_hash;
ms.amount = TALER_amount_hton (melt->amount);
transaction = TALER_JSON_from_ecdsa_sig (&ms.purpose,
&melt->coin_sig);
}
break; break;
case TALER_MINT_DB_TT_LOCK: case TALER_MINT_DB_TT_LOCK:
/* FIXME: add operation details to json reply */ {
break; type = "lock";
value = pos->details.lock->amount;
transaction = NULL;
GNUNET_break (0); /* #3625: Lock NOT implemented! */
break;
}
default:
GNUNET_assert (0);
} }
json_array_append_new (history,
json_pack ("{s:s, s:o}",
"type", type,
"amount", TALER_JSON_from_amount (value),
"signature", transaction));
} }
ret = TALER_MINT_reply_json_pack (connection, ret = TALER_MINT_reply_json_pack (connection,
MHD_HTTP_FORBIDDEN, MHD_HTTP_FORBIDDEN,
"{s:s}", "{s:s, s:o}",
"error", "insufficient funds"); "error", "insufficient funds",
"history", history);
return ret; return ret;
} }
@ -517,7 +561,7 @@ TALER_MINT_reply_withdraw_sign_success (struct MHD_Connection *connection,
char *sig_buf; char *sig_buf;
int ret; int ret;
/* FIXME: use TALER_JSON_from_sig here instead!? */ /* FIXME: use TALER_JSON_from_eddsa_sig here instead!? */
sig_buf_size = GNUNET_CRYPTO_rsa_signature_encode (collectable->sig, sig_buf_size = GNUNET_CRYPTO_rsa_signature_encode (collectable->sig,
&sig_buf); &sig_buf);
sig_json = TALER_JSON_from_data (sig_buf, sig_json = TALER_JSON_from_data (sig_buf,
@ -561,7 +605,7 @@ TALER_MINT_reply_refresh_melt_success (struct MHD_Connection *connection,
body.kappa = htonl (kappa); body.kappa = htonl (kappa);
TALER_MINT_keys_sign (&body.purpose, TALER_MINT_keys_sign (&body.purpose,
&sig); &sig);
sig_json = TALER_JSON_from_sig (&body.purpose, &sig); sig_json = TALER_JSON_from_eddsa_sig (&body.purpose, &sig);
ret = TALER_MINT_reply_json_pack (connection, ret = TALER_MINT_reply_json_pack (connection,
MHD_HTTP_OK, MHD_HTTP_OK,
"{s:o, s:i}", "{s:o, s:i}",
@ -595,7 +639,7 @@ TALER_MINT_reply_refresh_commit_success (struct MHD_Connection *connection,
body.noreveal_index = htons (refresh_session->noreveal_index); body.noreveal_index = htons (refresh_session->noreveal_index);
TALER_MINT_keys_sign (&body.purpose, TALER_MINT_keys_sign (&body.purpose,
&sig); &sig);
sig_json = TALER_JSON_from_sig (&body.purpose, &sig); sig_json = TALER_JSON_from_eddsa_sig (&body.purpose, &sig);
GNUNET_assert (NULL != sig_json); GNUNET_assert (NULL != sig_json);
ret = TALER_MINT_reply_json_pack (connection, ret = TALER_MINT_reply_json_pack (connection,
MHD_HTTP_OK, MHD_HTTP_OK,

View File

@ -90,8 +90,8 @@ TALER_JSON_from_abs (struct GNUNET_TIME_Absolute stamp)
* @return the JSON reporesentation of the signature with purpose * @return the JSON reporesentation of the signature with purpose
*/ */
json_t * json_t *
TALER_JSON_from_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, TALER_JSON_from_eddsa_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
const struct GNUNET_CRYPTO_EddsaSignature *signature) const struct GNUNET_CRYPTO_EddsaSignature *signature)
{ {
json_t *root; json_t *root;
json_t *el; json_t *el;
@ -106,7 +106,37 @@ TALER_JSON_from_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
el = TALER_JSON_from_data (signature, el = TALER_JSON_from_data (signature,
sizeof (struct GNUNET_CRYPTO_EddsaSignature)); sizeof (struct GNUNET_CRYPTO_EddsaSignature));
json_object_set_new (root, "sig", el); json_object_set_new (root, "eddsa-sig", el);
return root;
}
/**
* Convert a signature (with purpose) to a JSON object representation.
*
* @param purpose purpose of the signature
* @param signature the signature
* @return the JSON reporesentation of the signature with purpose
*/
json_t *
TALER_JSON_from_ecdsa_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
const struct GNUNET_CRYPTO_EcdsaSignature *signature)
{
json_t *root;
json_t *el;
root = json_object ();
el = json_integer ((json_int_t) ntohl (purpose->size));
json_object_set_new (root, "size", el);
el = json_integer ((json_int_t) ntohl (purpose->purpose));
json_object_set_new (root, "purpose", el);
el = TALER_JSON_from_data (signature,
sizeof (struct GNUNET_CRYPTO_EddsaSignature));
json_object_set_new (root, "ecdsa-sig", el);
return root; return root;
} }
@ -133,6 +163,20 @@ TALER_JSON_from_data (const void *data, size_t size)
} }
/**
* Convert binary hash to a JSON string with the base32crockford
* encoding.
*
* @param hc binary data
* @return json string that encodes @a hc
*/
json_t *
TALER_JSON_from_hash (const struct GNUNET_HashCode *hc)
{
return TALER_JSON_from_data (hc, sizeof (struct GNUNET_HashCode));
}
/** /**
* Parse given JSON object to Amount * Parse given JSON object to Amount
* *