From ea3fac606311d1c14362d387960a4913182b629e Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 27 Jan 2015 23:06:23 +0100 Subject: [PATCH] starting to clean up /deposit logic --- src/mint/mint.h | 74 ++++++++++---- src/mint/mint_db.c | 49 ++++++---- src/mint/mint_db.h | 4 +- src/mint/taler-mint-httpd_db.c | 12 ++- src/mint/taler-mint-httpd_deposit.c | 147 ++++++++++++++++------------ src/mint/taler-mint-httpd_deposit.h | 8 +- src/mint/taler-mint-httpd_keys.c | 2 +- src/mint/taler-mint-httpd_keys.h | 2 +- src/mint/taler-mint-httpd_parsing.h | 8 +- 9 files changed, 188 insertions(+), 118 deletions(-) diff --git a/src/mint/mint.h b/src/mint/mint.h index 046b9c17e..05e966e1c 100644 --- a/src/mint/mint.h +++ b/src/mint/mint.h @@ -28,6 +28,7 @@ #include #include #include +#include #include "taler_util.h" #include "taler_signatures.h" @@ -71,13 +72,15 @@ struct TALER_CoinPublicInfo */ struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub; - /* - * The public key signifying the coin's denomination. + /** + * Public key representing the denomination of the coin + * that is being deposited. */ struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub; /** - * Signature over coin_pub by denom_pub. + * (Unblinded) signature over @e coin_pub with @e denom_pub, + * which demonstrates that the coin is valid. */ struct GNUNET_CRYPTO_rsa_Signature *denom_sig; }; @@ -236,33 +239,62 @@ struct KnownCoin */ struct Deposit { - /* FIXME: should be TALER_CoinPublicInfo */ - struct GNUNET_CRYPTO_EddsaPublicKey coin_pub; + /** + * Information about the coin that is being deposited. + */ + struct TALER_CoinPublicInfo coin; - struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub; + /** + * EdDSA signature affirming that the customer intends + * this coin to be deposited at the merchant identified + * by @e h_wire in relation to the contract identified + * by @e h_contract. + */ + struct GNUNET_CRYPTO_EddsaSignature csig; - struct GNUNET_CRYPTO_rsa_Signature *coin_sig; + /** + * Public key of the merchant. Enables later identification + * of the merchant in case of a need to rollback transactions. + */ + struct GNUNET_CRYPTO_EddsaPublicKey merchant_pub; - struct GNUNET_CRYPTO_rsa_Signature *ubsig; // ??? + /** + * Hash over the contract between merchant and customer + * (remains unknown to the Mint). + */ + struct GNUNET_HashCode h_contract; + + /** + * Hash of the (canonical) representation of @e wire, used + * to check the signature on the request. Generated by + * the mint from the detailed wire data provided by the + * merchant. + */ + struct GNUNET_HashCode h_wire; + + /** + * Detailed wire information for executing the transaction. + */ + const json_t *wire; + + /** + * Merchant-generated transaction ID to detect duplicate + * transactions. + */ + uint64_t transaction_id; + + /** + * Fraction of the coin's remaining value to be deposited. + * The coin is identified by @e coin_pub. + */ + struct TALER_AmountNBO amount; /** * Type of the deposit (also purpose of the signature). Either * #TALER_SIGNATURE_DEPOSIT or #TALER_SIGNATURE_INCREMENTAL_DEPOSIT. */ - // struct TALER_RSA_SignaturePurpose purpose; // FIXME: bad type! + uint32_t purpose; // FIXME: bad type, use ENUM! - uint64_t transaction_id; - - struct TALER_AmountNBO amount; - - struct GNUNET_CRYPTO_EddsaPublicKey merchant_pub; - - struct GNUNET_HashCode h_contract; - - struct GNUNET_HashCode h_wire; - - /* TODO: uint16_t wire_size */ - char wire[]; /* string encoded wire JSON object */ }; diff --git a/src/mint/mint_db.c b/src/mint/mint_db.c index f6fff7e5c..4c836bf92 100644 --- a/src/mint/mint_db.c +++ b/src/mint/mint_db.c @@ -1840,17 +1840,21 @@ TALER_MINT_DB_insert_deposit (PGconn *db_conn, const struct Deposit *deposit) { struct TALER_DB_QueryParam params[]= { - TALER_DB_QUERY_PARAM_PTR (&deposit->coin_pub), - TALER_DB_QUERY_PARAM_PTR (&deposit->denom_pub), + TALER_DB_QUERY_PARAM_PTR (&deposit->coin.coin_pub), + TALER_DB_QUERY_PARAM_PTR (&deposit->coin.denom_pub), // FIXME! + TALER_DB_QUERY_PARAM_PTR (&deposit->coin.denom_sig), // FIXME! TALER_DB_QUERY_PARAM_PTR (&deposit->transaction_id), + TALER_DB_QUERY_PARAM_PTR (&deposit->purpose), // FIXME: enum Ok here? TALER_DB_QUERY_PARAM_PTR (&deposit->amount.value), TALER_DB_QUERY_PARAM_PTR (&deposit->amount.fraction), - TALER_DB_QUERY_PARAM_PTR_SIZED (deposit->amount.currency, strlen (deposit->amount.currency)), + TALER_DB_QUERY_PARAM_PTR_SIZED (deposit->amount.currency, + strlen (deposit->amount.currency)), TALER_DB_QUERY_PARAM_PTR (&deposit->merchant_pub), TALER_DB_QUERY_PARAM_PTR (&deposit->h_contract), TALER_DB_QUERY_PARAM_PTR (&deposit->h_wire), - TALER_DB_QUERY_PARAM_PTR (&deposit->coin_sig), - TALER_DB_QUERY_PARAM_PTR_SIZED (deposit->wire, strlen(deposit->wire)), + TALER_DB_QUERY_PARAM_PTR (&deposit->csig), + TALER_DB_QUERY_PARAM_PTR_SIZED (deposit->wire, + strlen ("FIXME")), // FIXME! json! TALER_DB_QUERY_PARAM_END }; PGresult *result; @@ -1869,18 +1873,19 @@ TALER_MINT_DB_insert_deposit (PGconn *db_conn, int TALER_MINT_DB_get_deposit (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *coin_pub, - struct Deposit **r_deposit) + const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, + struct Deposit *deposit) { struct TALER_DB_QueryParam params[] = { TALER_DB_QUERY_PARAM_PTR (coin_pub), TALER_DB_QUERY_PARAM_END }; PGresult *result; - struct Deposit *deposit; - deposit = NULL; - result = TALER_DB_exec_prepared (db_conn, "get_deposit", params); + memset (deposit, 0, sizeof (struct Deposit)); + result = TALER_DB_exec_prepared (db_conn, + "get_deposit", + params); if (PGRES_TUPLES_OK != PQresultStatus (result)) { break_db_err (result); @@ -1900,19 +1905,21 @@ TALER_MINT_DB_get_deposit (PGconn *db_conn, } { - deposit = GNUNET_malloc (sizeof (struct Deposit)); /* Without wire data */ - char *sig_buf; - size_t sig_buf_size; + char *denom_sig_buf; + size_t denom_sig_buf_size; char *dk_buf; size_t dk_buf_size; + struct TALER_DB_ResultSpec rs[] = { - TALER_DB_RESULT_SPEC ("coin_pub", &deposit->coin_pub), + TALER_DB_RESULT_SPEC ("coin_pub", &deposit->coin.coin_pub), TALER_DB_RESULT_SPEC_VAR ("denom_pub", &dk_buf, &dk_buf_size), - TALER_DB_RESULT_SPEC_VAR ("coin_sig", &sig_buf, &sig_buf_size), + TALER_DB_RESULT_SPEC_VAR ("denom_sig", &denom_sig_buf, &denom_sig_buf_size), TALER_DB_RESULT_SPEC ("transaction_id", &deposit->transaction_id), TALER_DB_RESULT_SPEC ("merchant_pub", &deposit->merchant_pub), TALER_DB_RESULT_SPEC ("h_contract", &deposit->h_contract), TALER_DB_RESULT_SPEC ("h_wire", &deposit->h_wire), + TALER_DB_RESULT_SPEC ("purpose", &deposit->purpose), + // FIXME: many fields missing... TALER_DB_RESULT_SPEC_END }; EXITIF (GNUNET_OK != @@ -1923,15 +1930,15 @@ TALER_MINT_DB_get_deposit (PGconn *db_conn, "amount_fraction", "amount_currency", &deposit->amount)); - deposit->coin_sig = GNUNET_CRYPTO_rsa_signature_decode (sig_buf, - sig_buf_size); - deposit->denom_pub = GNUNET_CRYPTO_rsa_public_key_decode (dk_buf, - dk_buf_size); - // deposit->purpose = htonl (TALER_SIGNATURE_DEPOSIT); // FIXME: struct Deposit not nice + deposit->coin.denom_sig + = GNUNET_CRYPTO_rsa_signature_decode (denom_sig_buf, + denom_sig_buf_size); + deposit->coin.denom_pub + = GNUNET_CRYPTO_rsa_public_key_decode (dk_buf, + dk_buf_size); } PQclear (result); - *r_deposit = deposit; return GNUNET_OK; EXITIF_exit: diff --git a/src/mint/mint_db.h b/src/mint/mint_db.h index e0c2e2e0a..3a62f1835 100644 --- a/src/mint/mint_db.h +++ b/src/mint/mint_db.h @@ -258,8 +258,8 @@ TALER_MINT_DB_insert_deposit (PGconn *db_conn, int TALER_MINT_DB_get_deposit (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *coin_pub, - struct Deposit **r_deposit); + const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, + struct Deposit *r_deposit); int diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index baf5d12aa..fcc6d915f 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -54,7 +54,7 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, const struct Deposit *deposit) { PGconn *db_conn; - struct Deposit *existing_deposit; + struct Deposit existing_deposit; int res; if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) @@ -63,13 +63,15 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, return TALER_MINT_reply_internal_db_error (connection); } res = TALER_MINT_DB_get_deposit (db_conn, - &deposit->coin_pub, + &deposit->coin.coin_pub, &existing_deposit); if (GNUNET_YES == res) { // FIXME: memory leak // FIXME: memcmp will not actually work here - if (0 == memcmp (existing_deposit, deposit, sizeof (struct Deposit))) + if (0 == memcmp (&existing_deposit, + deposit, + sizeof (struct Deposit))) return TALER_MINT_reply_deposit_success (connection, deposit); // FIXME: in the future, check if there's enough credits // left on the coin. For now: refuse @@ -93,7 +95,9 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, int res; struct TALER_CoinPublicInfo coin_info; - res = TALER_MINT_DB_get_known_coin (db_conn, &coin_info.coin_pub, &known_coin); + res = TALER_MINT_DB_get_known_coin (db_conn, + &coin_info.coin_pub, + &known_coin); if (GNUNET_YES == res) { // coin must have been refreshed diff --git a/src/mint/taler-mint-httpd_deposit.c b/src/mint/taler-mint-httpd_deposit.c index 4a3713c5b..ed0eca8bb 100644 --- a/src/mint/taler-mint-httpd_deposit.c +++ b/src/mint/taler-mint-httpd_deposit.c @@ -59,26 +59,6 @@ verify_and_execute_deposit (struct MHD_Connection *connection, const struct Deposit *deposit) { struct MintKeyState *key_state; - struct TALER_CoinPublicInfo coin_info; - - memcpy (&coin_info.coin_pub, - &deposit->coin_pub, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); - coin_info.denom_pub = deposit->denom_pub; - coin_info.denom_sig = deposit->ubsig; - - key_state = TALER_MINT_key_state_acquire (); - if (GNUNET_YES != - TALER_MINT_test_coin_valid (key_state, - &coin_info)) - { - TALER_MINT_key_state_release (key_state); - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", "Coin is not valid"); - } - TALER_MINT_key_state_release (key_state); /* FIXME: verify coin signature! */ /* @@ -88,11 +68,25 @@ verify_and_execute_deposit (struct MHD_Connection *connection, &deposit->coin_pub)) { resp = json_pack ("{s:s}", "error", "Signature verfication failed"); - resp_code = MHD_HTTP_NOT_FOUND; - goto EXITIF_exit; + return TALER_MINT_reply_arg_invalid (connection, + "csig"); } */ + key_state = TALER_MINT_key_state_acquire (); + if (GNUNET_YES != + TALER_MINT_test_coin_valid (key_state, + &deposit->coin)) + { + LOG_WARNING ("Invalid coin passed for /deposit\n"); + TALER_MINT_key_state_release (key_state); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", "Coin is not valid"); + } + TALER_MINT_key_state_release (key_state); + return TALER_MINT_db_execute_deposit (connection, deposit); } @@ -101,12 +95,12 @@ verify_and_execute_deposit (struct MHD_Connection *connection, /** * Handle a "/deposit" request. This function parses the * JSON information and then calls #verify_and_execute_deposit() - * to verify the data and execute the deposit. + * to verify the signatures and execute the deposit. * * @param connection the MHD connection to handle * @param root root of the posted JSON * @param purpose is this a #TALER_SIGNATURE_DEPOSIT or - * #TALER_SIGNATURE_INCREMENTAL_DEPOSIT + * #TALER_SIGNATURE_INCREMENTAL_DEPOSIT // FIXME: bad type, use enum! * @param wire json describing the wire details (?) * @return MHD result code */ @@ -116,61 +110,88 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection, uint32_t purpose, const json_t *wire) { - struct Deposit *deposit; + int res; + struct Deposit deposit; char *wire_enc; size_t len; - int res; + struct GNUNET_MINT_ParseFieldSpec spec[] = { + TALER_MINT_PARSE_VARIABLE ("denom_pub"), + TALER_MINT_PARSE_VARIABLE ("ubsig"), + TALER_MINT_PARSE_FIXED ("coin_pub", &deposit.coin.coin_pub), + TALER_MINT_PARSE_FIXED ("merchant_pub", &deposit.merchant_pub), + TALER_MINT_PARSE_FIXED ("H_a", &deposit.h_contract), + TALER_MINT_PARSE_FIXED ("H_wire", &deposit.h_wire), + TALER_MINT_PARSE_FIXED ("csig", &deposit.csig), + TALER_MINT_PARSE_FIXED ("transaction_id", &deposit.transaction_id), + TALER_MINT_PARSE_END + }; - // FIXME: `struct Deposit` is clearly ill-defined, we should - // not have to do this... + memset (&deposit, 0, sizeof (deposit)); + res = TALER_MINT_parse_json_data (connection, + root, + spec); + if (GNUNET_SYSERR == res) + return MHD_NO; /* hard failure */ + if (GNUNET_NO == res) + return MHD_YES; /* failure */ + deposit.coin.denom_pub + = GNUNET_CRYPTO_rsa_public_key_decode (spec[0].destination, + spec[0].destination_size_out); + if (NULL == deposit.coin.denom_pub) + { + LOG_WARNING ("Failed to parse denomination key for /deposit request\n"); + TALER_MINT_release_parsed_data (spec); + return TALER_MINT_reply_arg_invalid (connection, + "denom_pub"); + } + deposit.coin.denom_sig + = GNUNET_CRYPTO_rsa_signature_decode (spec[1].destination, + spec[1].destination_size_out); + if (NULL == deposit.coin.denom_sig) + { + LOG_WARNING ("Failed to parse unblinded signature for /deposit request\n"); + GNUNET_CRYPTO_rsa_public_key_free (deposit.coin.denom_pub); + TALER_MINT_release_parsed_data (spec); + return TALER_MINT_reply_arg_invalid (connection, + "denom_pub"); + } if (NULL == (wire_enc = json_dumps (wire, JSON_COMPACT | JSON_SORT_KEYS))) { - GNUNET_break_op (0); - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s}", - "error", "Bad format"); - + GNUNET_CRYPTO_rsa_public_key_free (deposit.coin.denom_pub); + GNUNET_CRYPTO_rsa_signature_free (deposit.coin.denom_sig); + LOG_WARNING ("Failed to parse JSON wire format specification for /deposit request\n"); + TALER_MINT_release_parsed_data (spec); + return TALER_MINT_reply_arg_invalid (connection, + "wire"); } len = strlen (wire_enc) + 1; + GNUNET_CRYPTO_hash (wire_enc, + len, + &deposit.h_wire); GNUNET_free (wire_enc); - deposit = GNUNET_malloc (sizeof (struct Deposit) + len); - { - struct GNUNET_MINT_ParseFieldSpec spec[] = - { - TALER_MINT_PARSE_FIXED ("coin_pub", &deposit->coin_pub), - TALER_MINT_PARSE_FIXED ("denom_pub", &deposit->denom_pub), - TALER_MINT_PARSE_FIXED ("ubsig", &deposit->ubsig), - TALER_MINT_PARSE_FIXED ("merchant_pub", &deposit->merchant_pub), - TALER_MINT_PARSE_FIXED ("H_a", &deposit->h_contract), - TALER_MINT_PARSE_FIXED ("H_wire", &deposit->h_wire), - TALER_MINT_PARSE_FIXED ("csig", &deposit->coin_sig), - TALER_MINT_PARSE_FIXED ("transaction_id", &deposit->transaction_id), - TALER_MINT_PARSE_END - }; - res = TALER_MINT_parse_json_data (connection, - wire, /* FIXME: wire or root here? */ - spec); - if (GNUNET_SYSERR == res) - return MHD_NO; /* hard failure */ - if (GNUNET_NO == res) - return MHD_YES; /* failure */ + deposit.wire = wire; + deposit.purpose = purpose; - // deposit->purpose = htonl (purpose); // FIXME... - res = verify_and_execute_deposit (connection, - deposit); - TALER_MINT_release_parsed_data (spec); - } - GNUNET_free (deposit); + // FIXME: deposit.amount not initialized! + + res = verify_and_execute_deposit (connection, + &deposit); + GNUNET_CRYPTO_rsa_public_key_free (deposit.coin.denom_pub); + GNUNET_CRYPTO_rsa_signature_free (deposit.coin.denom_sig); + TALER_MINT_release_parsed_data (spec); return res; } /** - * Handle a "/deposit" request. Parses the JSON in the post and, if + * Handle a "/deposit" request. Parses the JSON in the post to find + * the "type" (either DIRECT_DEPOSIT or INCREMENTAL_DEPOSIT), and, if * successful, passes the JSON data to - * #parse_and_handle_deposit_request(). + * #parse_and_handle_deposit_request() to further check the details + * of the operation specified in the "wire" field of the JSON data. + * If everything checks out, this will ultimately lead to the + * "/deposit" being executed, or rejected. * * @param rh context of the handler * @param connection the MHD connection to handle diff --git a/src/mint/taler-mint-httpd_deposit.h b/src/mint/taler-mint-httpd_deposit.h index dd7b8c133..c6d628050 100644 --- a/src/mint/taler-mint-httpd_deposit.h +++ b/src/mint/taler-mint-httpd_deposit.h @@ -29,7 +29,13 @@ /** - * Handle a "/deposit" request + * Handle a "/deposit" request. Parses the JSON in the post to find + * the "type" (either DIRECT_DEPOSIT or INCREMENTAL_DEPOSIT), and, if + * successful, passes the JSON data to + * #parse_and_handle_deposit_request() to further check the details + * of the operation specified in the "wire" field of the JSON data. + * If everything checks out, this will ultimately lead to the + * "/deposit" being executed, or rejected. * * @param rh context of the handler * @param connection the MHD connection to handle diff --git a/src/mint/taler-mint-httpd_keys.c b/src/mint/taler-mint-httpd_keys.c index 28924baef..359357da9 100644 --- a/src/mint/taler-mint-httpd_keys.c +++ b/src/mint/taler-mint-httpd_keys.c @@ -379,7 +379,7 @@ TALER_MINT_get_denom_key (const struct MintKeyState *key_state, */ int TALER_MINT_test_coin_valid (const struct MintKeyState *key_state, - struct TALER_CoinPublicInfo *coin_public_info) + const struct TALER_CoinPublicInfo *coin_public_info) { struct TALER_MINT_DenomKeyIssuePriv *dki; struct GNUNET_HashCode c_hash; diff --git a/src/mint/taler-mint-httpd_keys.h b/src/mint/taler-mint-httpd_keys.h index 4672c1359..4182c25ea 100644 --- a/src/mint/taler-mint-httpd_keys.h +++ b/src/mint/taler-mint-httpd_keys.h @@ -123,7 +123,7 @@ TALER_MINT_get_denom_key (const struct MintKeyState *key_state, */ int TALER_MINT_test_coin_valid (const struct MintKeyState *key_state, - struct TALER_CoinPublicInfo *coin_public_info); + const struct TALER_CoinPublicInfo *coin_public_info); /** diff --git a/src/mint/taler-mint-httpd_parsing.h b/src/mint/taler-mint-httpd_parsing.h index c8e946077..9c4d8aafe 100644 --- a/src/mint/taler-mint-httpd_parsing.h +++ b/src/mint/taler-mint-httpd_parsing.h @@ -40,14 +40,14 @@ * @param upload_data the POST data * @param upload_data_size number of bytes in @a upload_data * @param json the JSON object for a completed request - * @returns - * GNUNET_YES if json object was parsed or at least + * @return + * #GNUNET_YES if json object was parsed or at least * may be parsed in the future (call again); * `*json` will be NULL if we need to be called again, * and non-NULL if we are done. - * GNUNET_NO is request incomplete or invalid + * #GNUNET_NO is request incomplete or invalid * (error message was generated) - * GNUNET_SYSERR on internal error + * #GNUNET_SYSERR on internal error * (we could not even queue an error message, * close HTTP session with MHD_NO) */