From 579f465c9b2ed1cd4602ee102073d633fda60cb9 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 9 Mar 2015 12:29:41 +0100 Subject: [PATCH] implementing #3632: generate proof of insufficient funds by converting transaction history to JSON --- src/include/taler_json_lib.h | 30 ++++++- src/include/taler_signatures.h | 115 +++++++++++++++++--------- src/mint/mint_db.h | 13 +-- src/mint/taler-mint-httpd_db.c | 15 ++-- src/mint/taler-mint-httpd_db.h | 5 +- src/mint/taler-mint-httpd_refresh.c | 4 +- src/mint/taler-mint-httpd_responses.c | 68 ++++++++++++--- src/util/json.c | 50 ++++++++++- 8 files changed, 228 insertions(+), 72 deletions(-) diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h index f0ae923f4..2b9d51875 100644 --- a/src/include/taler_json_lib.h +++ b/src/include/taler_json_lib.h @@ -60,8 +60,20 @@ TALER_JSON_from_abs (struct GNUNET_TIME_Absolute stamp); * @return the JSON reporesentation of the signature with purpose */ json_t * -TALER_JSON_from_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, - const struct GNUNET_CRYPTO_EddsaSignature *signature); +TALER_JSON_from_eddsa_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + 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); +/** + * 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 * @@ -119,7 +142,8 @@ TALER_JSON_to_data (json_t *json, * @return 1 if correctly formatted; 0 if not */ 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_ */ diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index 905d1ed23..8984165e6 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -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 diff --git a/src/mint/mint_db.h b/src/mint/mint_db.h index 74f0c2d14..c3a300a57 100644 --- a/src/mint/mint_db.h +++ b/src/mint/mint_db.h @@ -222,8 +222,6 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db, const struct TALER_Amount balance, const struct GNUNET_TIME_Absolute expiry); -/* FIXME: need call to convert CollectableBlindcoin to JSON (#3527) */ - /** * 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. - * 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. @@ -544,8 +540,15 @@ struct RefreshMelt /* FIXME: name to make it clearer this is about ONE coin! */ */ 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? + * 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; diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index e3f7b754c..ed9119313 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -22,10 +22,6 @@ * - actually abstract DB implementation (i.e. via plugin logic) * (this file should remain largely unchanged with the exception * 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 @@ -354,8 +350,8 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, break; } } - - /* FIXME: good place to assert deposit_total > withdraw_total... */ + GNUNET_break (0 > TALER_amount_cmp (withdraw_total, + deposit_total)); balance = TALER_amount_subtract (deposit_total, withdraw_total); 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, "Internal error"); } - - // FIXME: can we avoid the cast? collectable.denom_pub = (struct GNUNET_CRYPTO_rsa_PublicKey *) denomination_pub; collectable.sig = sig; collectable.reserve_pub = *reserve; @@ -430,6 +424,7 @@ static int refresh_accept_melts (struct MHD_Connection *connection, PGconn *db_conn, const struct MintKeyState *key_state, + const struct GNUNET_HashCode *melt_hash, const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, const struct TALER_CoinPublicInfo *coin_public_info, const struct MeltDetails *coin_details, @@ -474,6 +469,7 @@ refresh_accept_melts (struct MHD_Connection *connection, melt.coin = *coin_public_info; melt.coin_sig = coin_details->melt_sig; + melt.melt_hash = *melt_hash; melt.amount = coin_details->melt_amount; if (GNUNET_OK != 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. * * @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 client_signature signature of the client (matching @a refresh_session_pub) * over the melting request @@ -508,6 +505,7 @@ refresh_accept_melts (struct MHD_Connection *connection, */ int 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_EddsaSignature *client_signature, unsigned int num_new_denoms, @@ -558,6 +556,7 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, (res = refresh_accept_melts (connection, db_conn, key_state, + melt_hash, refresh_session_pub, &coin_public_infos[i], &coin_melt_details[i], diff --git a/src/mint/taler-mint-httpd_db.h b/src/mint/taler-mint-httpd_db.h index b945a7f65..dcd5e6fa0 100644 --- a/src/mint/taler-mint-httpd_db.h +++ b/src/mint/taler-mint-httpd_db.h @@ -93,7 +93,8 @@ struct MeltDetails /** * 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; }; @@ -107,6 +108,7 @@ struct MeltDetails * melted and confirm the melting operation to the client. * * @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 client_signature signature of the client (matching @a refresh_session_pub) * over the melting request @@ -119,6 +121,7 @@ struct MeltDetails */ int 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_EddsaSignature *client_signature, unsigned int num_new_denoms, diff --git a/src/mint/taler-mint-httpd_refresh.c b/src/mint/taler-mint-httpd_refresh.c index cae001acc..5625dc8c7 100644 --- a/src/mint/taler-mint-httpd_refresh.c +++ b/src/mint/taler-mint-httpd_refresh.c @@ -195,9 +195,10 @@ handle_refresh_melt_binary (struct MHD_Connection *connection, GNUNET_CRYPTO_hash_context_finish (hash_context, &melt_hash); - body.melt_hash = melt_hash; body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT); 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 != (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) */ return TALER_MINT_db_execute_refresh_melt (connection, + &melt_hash, refresh_session_pub, NULL, /* FIXME: #3635! */ num_new_denoms, diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index 77cdb4078..ec8fdc03b 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -305,7 +305,7 @@ TALER_MINT_reply_deposit_success (struct MHD_Connection *connection, dc.merchant = *merchant; TALER_MINT_keys_sign (&dc.purpose, &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, MHD_HTTP_OK, "{s:s, s:o}", @@ -332,28 +332,72 @@ TALER_MINT_reply_insufficient_funds (struct MHD_Connection *connection, { const struct TALER_MINT_DB_TransactionList *pos; 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) { switch (pos->type) { 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: - /* 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; 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, MHD_HTTP_FORBIDDEN, - "{s:s}", - "error", "insufficient funds"); + "{s:s, s:o}", + "error", "insufficient funds", + "history", history); return ret; } @@ -517,7 +561,7 @@ TALER_MINT_reply_withdraw_sign_success (struct MHD_Connection *connection, char *sig_buf; 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); 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); TALER_MINT_keys_sign (&body.purpose, &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, MHD_HTTP_OK, "{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); TALER_MINT_keys_sign (&body.purpose, &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); ret = TALER_MINT_reply_json_pack (connection, MHD_HTTP_OK, diff --git a/src/util/json.c b/src/util/json.c index 02591d7bf..38f459200 100644 --- a/src/util/json.c +++ b/src/util/json.c @@ -90,8 +90,8 @@ TALER_JSON_from_abs (struct GNUNET_TIME_Absolute stamp) * @return the JSON reporesentation of the signature with purpose */ json_t * -TALER_JSON_from_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, - const struct GNUNET_CRYPTO_EddsaSignature *signature) +TALER_JSON_from_eddsa_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + const struct GNUNET_CRYPTO_EddsaSignature *signature) { json_t *root; json_t *el; @@ -106,7 +106,37 @@ TALER_JSON_from_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, el = TALER_JSON_from_data (signature, 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; } @@ -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 *