From ed51946442e5e22a7dea68f14ff2bf563503c755 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 19 Jan 2015 21:53:23 +0100 Subject: [PATCH] more code refactoring to separate parsing, db and response generation nicely --- src/include/taler_json_lib.h | 20 +- src/mint/mint.h | 192 +++++++++++++- src/mint/mint_db.h | 143 +---------- src/mint/taler-mint-httpd.c | 2 +- src/mint/taler-mint-httpd_db.c | 278 +++++++++++++++++++-- src/mint/taler-mint-httpd_db.h | 68 +++-- src/mint/taler-mint-httpd_keys.c | 2 +- src/mint/taler-mint-httpd_keys.h | 2 +- src/mint/taler-mint-httpd_refresh.c | 18 +- src/mint/taler-mint-httpd_responses.c | 62 +++++ src/mint/taler-mint-httpd_responses.h | 25 +- src/mint/taler-mint-httpd_withdraw.c | 345 +++----------------------- src/util/json.c | 28 +++ 13 files changed, 652 insertions(+), 533 deletions(-) diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h index b224c4b33..ec354cce7 100644 --- a/src/include/taler_json_lib.h +++ b/src/include/taler_json_lib.h @@ -27,8 +27,7 @@ /** - * Convert a TALER amount to a JSON - * object. + * Convert a TALER amount to a JSON object. * * @param amount the amount * @return a json object describing the amount @@ -47,6 +46,17 @@ json_t * TALER_JSON_from_abs (struct GNUNET_TIME_Absolute stamp); +/** + * 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_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + const struct GNUNET_CRYPTO_EddsaSignature *signature); + /** * Convert binary data to a JSON string @@ -65,7 +75,7 @@ TALER_JSON_from_data (const void *data, size_t size); * * @param json the json object representing Amount * @param r_amount where the amount has to be written - * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error + * @return #GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error */ int TALER_JSON_to_amount (json_t *json, @@ -76,7 +86,7 @@ TALER_JSON_to_amount (json_t *json, * * @param json the json object representing absolute time in seconds * @param r_abs where the time has to be written - * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error + * @return #GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error */ int TALER_JSON_to_abs (json_t *json, @@ -88,7 +98,7 @@ TALER_JSON_to_abs (json_t *json, * @param json the json object representing data * @param out the pointer to hold the parsed data. * @param out_size the size of r_data. - * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error + * @return #GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error */ int TALER_JSON_to_data (json_t *json, diff --git a/src/mint/mint.h b/src/mint/mint.h index 644a9d292..39dda7d5f 100644 --- a/src/mint/mint.h +++ b/src/mint/mint.h @@ -13,14 +13,15 @@ You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, If not, see */ - /** - * @file taler_mint.h + * @file mint.h * @brief Common functionality for the mint * @author Florian Dold * @author Benedikt Mueller + * + * TODO: + * - revisit and document `struct Deposit` members. */ - #ifndef _MINT_H #define _MINT_H @@ -55,11 +56,195 @@ struct TALER_MINT_DenomKeyIssuePriv * not available. */ struct TALER_RSA_PrivateKey *denom_priv; + struct TALER_MINT_DenomKeyIssue issue; }; +/** + * Public information about a coin. + */ +struct TALER_CoinPublicInfo +{ + /** + * The coin's public key. + */ + struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub; + + /* + * The public key signifying the coin's denomination. + */ + struct TALER_RSA_PublicKeyBinaryEncoded denom_pub; + + /** + * Signature over coin_pub by denom_pub. + */ + struct TALER_RSA_Signature denom_sig; +}; + + + + + + + +struct CollectableBlindcoin +{ + struct TALER_RSA_BlindedSignaturePurpose ev; + struct TALER_RSA_Signature ev_sig; + struct TALER_RSA_PublicKeyBinaryEncoded denom_pub; + struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; + struct GNUNET_CRYPTO_EddsaSignature reserve_sig; +}; + + +struct RefreshSession +{ + int has_commit_sig; + struct GNUNET_CRYPTO_EddsaSignature commit_sig; + struct GNUNET_CRYPTO_EddsaPublicKey session_pub; + uint16_t num_oldcoins; + uint16_t num_newcoins; + uint16_t kappa; + uint16_t noreveal_index; + uint8_t reveal_ok; +}; + + +#define TALER_REFRESH_SHARED_SECRET_LENGTH (sizeof (struct GNUNET_HashCode)) +#define TALER_REFRESH_LINK_LENGTH (sizeof (struct LinkData)) + +struct RefreshCommitLink +{ + struct GNUNET_CRYPTO_EddsaPublicKey session_pub; + struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub; + uint16_t cnc_index; + uint16_t oldcoin_index; + char shared_secret_enc[sizeof (struct GNUNET_HashCode)]; +}; + +struct LinkData +{ + struct GNUNET_CRYPTO_EcdsaPrivateKey coin_priv; + struct TALER_RSA_BlindingKeyBinaryEncoded bkey_enc; +}; + + +GNUNET_NETWORK_STRUCT_BEGIN + +struct SharedSecretEnc +{ + char data[TALER_REFRESH_SHARED_SECRET_LENGTH]; +}; + + +struct LinkDataEnc +{ + char data[sizeof (struct LinkData)]; +}; + +GNUNET_NETWORK_STRUCT_END + +struct RefreshCommitCoin +{ + struct GNUNET_CRYPTO_EddsaPublicKey session_pub; + struct TALER_RSA_BlindedSignaturePurpose coin_ev; + uint16_t cnc_index; + uint16_t newcoin_index; + char link_enc[sizeof (struct LinkData)]; +}; + + +struct KnownCoin +{ + struct TALER_CoinPublicInfo public_info; + struct TALER_Amount expended_balance; + int is_refreshed; + /** + * Refreshing session, only valid if + * is_refreshed==1. + */ + struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub; +}; + + +/** + * Specification for a /deposit operation. + */ +struct Deposit +{ + /* FIXME: should be TALER_CoinPublicInfo */ + struct GNUNET_CRYPTO_EddsaPublicKey coin_pub; + + struct TALER_RSA_PublicKeyBinaryEncoded denom_pub; + + struct TALER_RSA_Signature coin_sig; + + struct TALER_RSA_Signature ubsig; + + /** + * Type of the deposit (also purpose of the signature). Either + * #TALER_SIGNATURE_DEPOSIT or #TALER_SIGNATURE_INCREMENTAL_DEPOSIT. + */ + struct TALER_RSA_SignaturePurpose purpose; + + 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 */ + +}; + + +/** + * Reserve row. Corresponds to table 'reserves' in the mint's + * database. FIXME: not sure this is how we want to store this + * information. Also, may currently used in different ways in the + * code, so we might need to separate the struct into different ones + * depending on the context it is used in. + */ +struct Reserve +{ + /** + * Signature over the purse. + * Only valid if (blind_session_missing==GNUNET_YES). + */ + struct GNUNET_CRYPTO_EddsaSignature status_sig; + /** + * Signature with purpose TALER_SIGNATURE_PURSE. + * Only valid if (blind_session_missing==GNUNET_YES). + */ + struct GNUNET_CRYPTO_EccSignaturePurpose status_sig_purpose; + /** + * Signing key used to sign the purse. + * Only valid if (blind_session_missing==GNUNET_YES). + */ + struct GNUNET_CRYPTO_EddsaPublicKey status_sign_pub; + /** + * Withdraw public key, identifies the purse. + * Only the customer knows the corresponding private key. + */ + struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; + /** + * Remaining balance in the purse. + */ + struct TALER_AmountNBO balance; + + /** + * Expiration date for the purse. + */ + struct GNUNET_TIME_AbsoluteNBO expiration; +}; + @@ -153,4 +338,3 @@ TALER_TALER_DB_extract_amount_nbo (PGresult *result, unsigned int row, int indices[3], struct TALER_AmountNBO *denom_nbo); #endif /* _MINT_H */ - diff --git a/src/mint/mint_db.h b/src/mint/mint_db.h index 5fab7c02a..d5a74a45d 100644 --- a/src/mint/mint_db.h +++ b/src/mint/mint_db.h @@ -29,149 +29,8 @@ #include "taler_util.h" #include "taler_rsa.h" #include "taler-mint-httpd_db.h" +#include "mint.h" -/** - * Public information about a coin. - */ -struct TALER_CoinPublicInfo -{ - /** - * The coin's public key. - */ - struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub; - - /* - * The public key signifying the coin's denomination. - */ - struct TALER_RSA_PublicKeyBinaryEncoded denom_pub; - - /** - * Signature over coin_pub by denom_pub. - */ - struct TALER_RSA_Signature denom_sig; -}; - - - - - - -/** - * Reserve row. Corresponds to table 'reserves' in - * the mint's database. - */ -struct Reserve -{ - /** - * Signature over the purse. - * Only valid if (blind_session_missing==GNUNET_YES). - */ - struct GNUNET_CRYPTO_EddsaSignature status_sig; - /** - * Signature with purpose TALER_SIGNATURE_PURSE. - * Only valid if (blind_session_missing==GNUNET_YES). - */ - struct GNUNET_CRYPTO_EccSignaturePurpose status_sig_purpose; - /** - * Signing key used to sign the purse. - * Only valid if (blind_session_missing==GNUNET_YES). - */ - struct GNUNET_CRYPTO_EddsaPublicKey status_sign_pub; - /** - * Withdraw public key, identifies the purse. - * Only the customer knows the corresponding private key. - */ - struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; - /** - * Remaining balance in the purse. - */ - struct TALER_AmountNBO balance; - - /** - * Expiration date for the purse. - */ - struct GNUNET_TIME_AbsoluteNBO expiration; -}; - - -struct CollectableBlindcoin -{ - struct TALER_RSA_BlindedSignaturePurpose ev; - struct TALER_RSA_Signature ev_sig; - struct TALER_RSA_PublicKeyBinaryEncoded denom_pub; - struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; - struct GNUNET_CRYPTO_EddsaSignature reserve_sig; -}; - - -struct RefreshSession -{ - int has_commit_sig; - struct GNUNET_CRYPTO_EddsaSignature commit_sig; - struct GNUNET_CRYPTO_EddsaPublicKey session_pub; - uint16_t num_oldcoins; - uint16_t num_newcoins; - uint16_t kappa; - uint16_t noreveal_index; - uint8_t reveal_ok; -}; - - -#define TALER_REFRESH_SHARED_SECRET_LENGTH (sizeof (struct GNUNET_HashCode)) -#define TALER_REFRESH_LINK_LENGTH (sizeof (struct LinkData)) - -struct RefreshCommitLink -{ - struct GNUNET_CRYPTO_EddsaPublicKey session_pub; - struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub; - uint16_t cnc_index; - uint16_t oldcoin_index; - char shared_secret_enc[sizeof (struct GNUNET_HashCode)]; -}; - -struct LinkData -{ - struct GNUNET_CRYPTO_EcdsaPrivateKey coin_priv; - struct TALER_RSA_BlindingKeyBinaryEncoded bkey_enc; -}; - - -GNUNET_NETWORK_STRUCT_BEGIN - -struct SharedSecretEnc -{ - char data[TALER_REFRESH_SHARED_SECRET_LENGTH]; -}; - - -struct LinkDataEnc -{ - char data[sizeof (struct LinkData)]; -}; - -GNUNET_NETWORK_STRUCT_END - -struct RefreshCommitCoin -{ - struct GNUNET_CRYPTO_EddsaPublicKey session_pub; - struct TALER_RSA_BlindedSignaturePurpose coin_ev; - uint16_t cnc_index; - uint16_t newcoin_index; - char link_enc[sizeof (struct LinkData)]; -}; - - -struct KnownCoin -{ - struct TALER_CoinPublicInfo public_info; - struct TALER_Amount expended_balance; - int is_refreshed; - /** - * Refreshing session, only valid if - * is_refreshed==1. - */ - struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub; -}; int TALER_MINT_DB_prepare (PGconn *db_conn); diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c index 2c3b3d2e1..9dc03cdea 100644 --- a/src/mint/taler-mint-httpd.c +++ b/src/mint/taler-mint-httpd.c @@ -28,7 +28,6 @@ #include #include #include "mint.h" -#include "mint_db.h" #include "taler_signatures.h" #include "taler_rsa.h" #include "taler_json_lib.h" @@ -38,6 +37,7 @@ #include "taler-mint-httpd_deposit.h" #include "taler-mint-httpd_withdraw.h" #include "taler-mint-httpd_refresh.h" +#include "mint_db.h" /** diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index 7a78f93ea..f3a22911c 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -25,12 +25,15 @@ * - /deposit: check for leaks */ #include "platform.h" +#include +#include #include "taler-mint-httpd_db.h" #include "taler_signatures.h" +#include "taler-mint-httpd_keys.h" #include "taler-mint-httpd_responses.h" #include "mint_db.h" #include "mint.h" -#include +#include "taler_json_lib.h" /** @@ -48,43 +51,39 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, const struct Deposit *deposit) { PGconn *db_conn; + struct Deposit *existing_deposit; + int res; if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) { GNUNET_break (0); return TALER_MINT_reply_internal_error (connection, - "Failed to connect to database\n"); + "Failed to connect to database"); + } + res = TALER_MINT_DB_get_deposit (db_conn, + &deposit->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))) + 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 + // FIXME: return more information here + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_FORBIDDEN, + "{s:s}", + "error", + "double spending"); } + if (GNUNET_SYSERR == res) { - struct Deposit *existing_deposit; - int res; - - res = TALER_MINT_DB_get_deposit (db_conn, - &deposit->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))) - 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 - // FIXME: return more information here - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_FORBIDDEN, - "{s:s}", - "error", - "double spending"); - } - - if (GNUNET_SYSERR == res) - { GNUNET_break (0); /* FIXME: return error message to client via MHD! */ return MHD_NO; - } } { @@ -133,3 +132,228 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, TALER_MINT_DB_commit (db_conn); return TALER_MINT_reply_deposit_success (connection, deposit); } + + +/** + * Sign a reserve's status with the current signing key. + * FIXME: not sure why we do this. Should just return + * existing list of operations on the reserve. + * + * @param reserve the reserve to sign + * @param key_state the key state containing the current + * signing private key + */ +static void +sign_reserve (struct Reserve *reserve, + struct MintKeyState *key_state) +{ + reserve->status_sign_pub = key_state->current_sign_key_issue.issue.signkey_pub; + reserve->status_sig_purpose.purpose = htonl (TALER_SIGNATURE_RESERVE_STATUS); + reserve->status_sig_purpose.size = htonl (sizeof (struct Reserve) - + offsetof (struct Reserve, status_sig_purpose)); + GNUNET_CRYPTO_eddsa_sign (&key_state->current_sign_key_issue.signkey_priv, + &reserve->status_sig_purpose, + &reserve->status_sig); +} + + +/** + * Execute a /withdraw/status. + * + * @param connection the MHD connection to handle + * @param reserve_pub public key of the reserve to check + * @return MHD result code + */ +int +TALER_MINT_db_execute_withdraw_status (struct MHD_Connection *connection, + const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub) +{ + PGconn *db_conn; + int res; + struct Reserve reserve; + struct MintKeyState *key_state; + int must_update = GNUNET_NO; + + + if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) + { + GNUNET_break (0); + return TALER_MINT_reply_internal_error (connection, + "Failed to connect to database"); + } + res = TALER_MINT_DB_get_reserve (db_conn, + reserve_pub, + &reserve); + /* check if these are really the matching error codes, + seems odd... */ + if (GNUNET_SYSERR == res) + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", + "Reserve not found"); + if (GNUNET_OK != res) + { + GNUNET_break (0); + return TALER_MINT_reply_internal_error (connection, + "Internal error"); + } + key_state = TALER_MINT_key_state_acquire (); + if (0 != memcmp (&key_state->current_sign_key_issue.issue.signkey_pub, + &reserve.status_sign_pub, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))) + { + sign_reserve (&reserve, key_state); + must_update = GNUNET_YES; + } + if ((GNUNET_YES == must_update) && + (GNUNET_OK != TALER_MINT_DB_update_reserve (db_conn, &reserve, !must_update))) + { + GNUNET_break (0); + return MHD_YES; + } + return TALER_MINT_reply_withdraw_status_success (connection, + &reserve); +} + + +/** + * Execute a /withdraw/sign. + * + * @param connection the MHD connection to handle + * @param wsrd_ro details about the withdraw request + * @return MHD result code + */ +int +TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, + const struct TALER_WithdrawRequest *wsrd_ro) +{ + PGconn *db_conn; + struct Reserve reserve; + struct MintKeyState *key_state; + struct CollectableBlindcoin collectable; + struct TALER_MINT_DenomKeyIssuePriv *dki; + struct TALER_RSA_Signature ev_sig; + struct TALER_Amount amount_required; + /* FIXME: the fact that we do this here is a sign that we + need to have different versions of this struct for + the different places it is used! */ + struct TALER_WithdrawRequest wsrd = *wsrd_ro; + int res; + + if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) + { + // FIXME: return 'internal error'? + GNUNET_break (0); + return MHD_NO; + } + + + res = TALER_MINT_DB_get_collectable_blindcoin (db_conn, + &wsrd.coin_envelope, + &collectable); + if (GNUNET_SYSERR == res) + { + // FIXME: return 'internal error' + GNUNET_break (0); + return MHD_NO; + } + + /* Don't sign again if we have already signed the coin */ + if (GNUNET_YES == res) + return TALER_MINT_reply_withdraw_sign_success (connection, + &collectable); + GNUNET_assert (GNUNET_NO == res); + res = TALER_MINT_DB_get_reserve (db_conn, + &wsrd.reserve_pub, + &reserve); + if (GNUNET_SYSERR == res) + { + // FIXME: return 'internal error' + GNUNET_break (0); + return MHD_NO; + } + if (GNUNET_NO == res) + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", + "Reserve not found"); + + // fill out all the missing info in the request before + // we can check the signature on the request + + wsrd.purpose.purpose = htonl (TALER_SIGNATURE_WITHDRAW); + wsrd.purpose.size = htonl (sizeof (struct TALER_WithdrawRequest) - + offsetof (struct TALER_WithdrawRequest, purpose)); + + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WITHDRAW, + &wsrd.purpose, + &wsrd.sig, + &wsrd.reserve_pub)) + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_UNAUTHORIZED, + "{s:s}", + "error", "Invalid Signature"); + + key_state = TALER_MINT_key_state_acquire (); + dki = TALER_MINT_get_denom_key (key_state, + &wsrd.denomination_pub); + TALER_MINT_key_state_release (key_state); + if (NULL == dki) + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", + "Denomination not found"); + + amount_required = TALER_amount_ntoh (dki->issue.value); + amount_required = TALER_amount_add (amount_required, + TALER_amount_ntoh (dki->issue.fee_withdraw)); + + if (0 < TALER_amount_cmp (amount_required, + TALER_amount_ntoh (reserve.balance))) + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_PAYMENT_REQUIRED, + "{s:s}", + "error", + "Insufficient funds"); + if (GNUNET_OK != + TALER_RSA_sign (dki->denom_priv, + &wsrd.coin_envelope, + sizeof (struct TALER_RSA_BlindedSignaturePurpose), + &ev_sig)) + { + // FIXME: return 'internal error' + GNUNET_break (0); + return MHD_NO; + } + + reserve.balance = TALER_amount_hton (TALER_amount_subtract (TALER_amount_ntoh (reserve.balance), + amount_required)); + if (GNUNET_OK != + TALER_MINT_DB_update_reserve (db_conn, + &reserve, + GNUNET_YES)) + { + // FIXME: return 'internal error' + GNUNET_break (0); + return MHD_NO; + } + + collectable.ev = wsrd.coin_envelope; + collectable.ev_sig = ev_sig; + collectable.reserve_pub = wsrd.reserve_pub; + collectable.reserve_sig = wsrd.sig; + if (GNUNET_OK != + TALER_MINT_DB_insert_collectable_blindcoin (db_conn, + &collectable)) + { + // FIXME: return 'internal error' + GNUNET_break (0); + return GNUNET_NO;; + } + return TALER_MINT_reply_withdraw_sign_success (connection, + &collectable); +} diff --git a/src/mint/taler-mint-httpd_db.h b/src/mint/taler-mint-httpd_db.h index 1829f3d37..a479576da 100644 --- a/src/mint/taler-mint-httpd_db.h +++ b/src/mint/taler-mint-httpd_db.h @@ -17,9 +17,6 @@ * @file mint/taler-mint_httpd_db.h * @brief Mint-specific database access * @author Chrisitan Grothoff - * - * TODO: - * - revisit and document `struct Deposit` members. */ #ifndef TALER_MINT_HTTPD_DB_H #define TALER_MINT_HTTPD_DB_H @@ -29,46 +26,13 @@ #include #include "taler_util.h" #include "taler_rsa.h" +#include "taler-mint-httpd_keys.h" +#include "mint.h" + /** - * Specification for a /deposit operation. - */ -struct Deposit -{ - /* FIXME: should be TALER_CoinPublicInfo */ - struct GNUNET_CRYPTO_EddsaPublicKey coin_pub; - - struct TALER_RSA_PublicKeyBinaryEncoded denom_pub; - - struct TALER_RSA_Signature coin_sig; - - struct TALER_RSA_Signature ubsig; - - /** - * Type of the deposit (also purpose of the signature). Either - * #TALER_SIGNATURE_DEPOSIT or #TALER_SIGNATURE_INCREMENTAL_DEPOSIT. - */ - struct TALER_RSA_SignaturePurpose purpose; - - 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 */ - -}; - - -/** - * Execute a deposit. The validity of the coin and signature + * Execute a /deposit. The validity of the coin and signature * have already been checked. The database must now check that * the coin is not (double or over) spent, and execute the * transaction (record details, generate success or failure response). @@ -82,4 +46,28 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, const struct Deposit *deposit); +/** + * Execute a /withdraw/status. + * + * @param connection the MHD connection to handle + * @param reserve_pub public key of the reserve to check + * @return MHD result code + */ +int +TALER_MINT_db_execute_withdraw_status (struct MHD_Connection *connection, + const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub); + + +/** + * Execute a /withdraw/sign. + * + * @param connection the MHD connection to handle + * @param wsrd details about the withdraw request + * @return MHD result code + */ +int +TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, + const struct TALER_WithdrawRequest *wsrd); + + #endif /* _NEURO_MINT_DB_H */ diff --git a/src/mint/taler-mint-httpd_keys.c b/src/mint/taler-mint-httpd_keys.c index 149e44d3f..8db32cd83 100644 --- a/src/mint/taler-mint-httpd_keys.c +++ b/src/mint/taler-mint-httpd_keys.c @@ -27,7 +27,6 @@ #include #include #include "mint.h" -#include "mint_db.h" #include "taler_signatures.h" #include "taler_rsa.h" #include "taler_json_lib.h" @@ -35,6 +34,7 @@ #include "taler-mint-httpd_keys.h" + /** * Mint key state. Never use directly, instead access via * #TALER_MINT_key_state_acquire and #TALER_MINT_key_state_release. diff --git a/src/mint/taler-mint-httpd_keys.h b/src/mint/taler-mint-httpd_keys.h index 4fd3d0bdd..c156cff66 100644 --- a/src/mint/taler-mint-httpd_keys.h +++ b/src/mint/taler-mint-httpd_keys.h @@ -26,7 +26,7 @@ #include #include #include "taler-mint-httpd.h" - +#include "mint.h" /** * Snapshot of the (coin and signing) diff --git a/src/mint/taler-mint-httpd_refresh.c b/src/mint/taler-mint-httpd_refresh.c index ab21e814c..883da8a85 100644 --- a/src/mint/taler-mint-httpd_refresh.c +++ b/src/mint/taler-mint-httpd_refresh.c @@ -19,6 +19,9 @@ * @author Florian Dold * @author Benedikt Mueller * @author Christian Grothoff + * + * TODO: + * - split properly into parsing, DB-ops and response generation */ #include "platform.h" #include @@ -70,6 +73,9 @@ sign_as_json (struct GNUNET_CRYPTO_EccSignaturePurpose *purpose) } +/** + * FIXME: document! + */ static int link_iter (void *cls, const struct LinkDataEnc *link_data_enc, @@ -246,9 +252,9 @@ check_confirm_signature (struct MHD_Connection *connection, * * @param connection the connection to send error responses to * @param root the JSON object to extract the coin info from - * @return GNUNET_YES if coin public info in JSON was valid - * GNUNET_NO otherwise - * GNUNET_SYSERR on internal error + * @return #GNUNET_YES if coin public info in JSON was valid + * #GNUNET_NO otherwise + * #GNUNET_SYSERR on internal error */ static int request_json_require_coin_public_info (struct MHD_Connection *connection, @@ -298,9 +304,9 @@ request_json_require_coin_public_info (struct MHD_Connection *connection, * @param root the JSON object * @param hash_context the hash context that will receive * the coin public keys of the melted coin - * @return a GNUnet result code, GNUNET_OK on success, - * GNUNET_NO if an error message was generated, - * GNUNET_SYSERR on internal errors (no response generated) + * @return #GNUNET_OK on success, + * #GNUNET_NO if an error message was generated, + * #GNUNET_SYSERR on internal errors (no response generated) */ static int refresh_accept_melts (struct MHD_Connection *connection, diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index 6ae219b63..78e239b7c 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -26,9 +26,14 @@ * TODO: * - when generating /deposit reply, do include signature of mint * to say that we accepted it (check reply format) + * - when generating /withdraw/status reply, which signature do + * we use there? Might want to instead return *all* signatures on the + * existig withdraw operations, instead of Mint's signature + * (check reply format, adjust `struct Reserve` if needed) */ #include "platform.h" #include "taler-mint-httpd_responses.h" +#include "taler_json_lib.h" /** @@ -229,4 +234,61 @@ TALER_MINT_reply_deposit_success (struct MHD_Connection *connection, "DEPOSIT_OK"); } + +/** + * Send reserve status information to client. + * + * @param connection connection to the client + * @param reserve reserve status information to return + * @return MHD result code + */ +int +TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection, + const struct Reserve *reserve) +{ + json_t *json; + + /* Convert the public information of a reserve (i.e. + excluding private key) to a JSON object. */ + json = json_object (); + json_object_set_new (json, + "balance", + TALER_JSON_from_amount (TALER_amount_ntoh (reserve->balance))); + json_object_set_new (json, + "expiration", + TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (reserve->expiration))); + json_object_set_new (json, + "signature", + TALER_JSON_from_sig (&reserve->status_sig_purpose, + &reserve->status_sig)); + + return TALER_MINT_reply_json (connection, + json, + MHD_HTTP_OK); +} + + +/** + * Send blinded coin information to client. + * + * @param connection connection to the client + * @param collectable blinded coin to return + * @return MHD result code + */ +int +TALER_MINT_reply_withdraw_sign_success (struct MHD_Connection *connection, + const struct CollectableBlindcoin *collectable) +{ + json_t *root = json_object (); + + json_object_set_new (root, "ev_sig", + TALER_JSON_from_data (&collectable->ev_sig, + sizeof (struct TALER_RSA_Signature))); + return TALER_MINT_reply_json (connection, + root, + MHD_HTTP_OK); +} + + + /* end of taler-mint-httpd_responses.c */ diff --git a/src/mint/taler-mint-httpd_responses.h b/src/mint/taler-mint-httpd_responses.h index 6dd8cda90..ce7557e1d 100644 --- a/src/mint/taler-mint-httpd_responses.h +++ b/src/mint/taler-mint-httpd_responses.h @@ -31,8 +31,8 @@ #include #include #include "taler-mint-httpd.h" +#include "taler-mint-httpd_db.h" #include "taler-mint-httpd_mhd.h" -#include "mint_db.h" /** @@ -135,5 +135,28 @@ TALER_MINT_reply_deposit_success (struct MHD_Connection *connection, const struct Deposit *deposit); +/** + * Send reserve status information to client. + * + * @param connection connection to the client + * @param reserve reserve status information to return + * @return MHD result code + */ +int +TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection, + const struct Reserve *reserve); + + +/** + * Send blinded coin information to client. + * + * @param connection connection to the client + * @param collectable blinded coin to return + * @return MHD result code + */ +int +TALER_MINT_reply_withdraw_sign_success (struct MHD_Connection *connection, + const struct CollectableBlindcoin *collectable); + #endif diff --git a/src/mint/taler-mint-httpd_withdraw.c b/src/mint/taler-mint-httpd_withdraw.c index cc0de7f6f..5259c7fbf 100644 --- a/src/mint/taler-mint-httpd_withdraw.c +++ b/src/mint/taler-mint-httpd_withdraw.c @@ -19,6 +19,9 @@ * @author Florian Dold * @author Benedikt Mueller * @author Christian Grothoff + * + * TODO: + * - support variable-size RSA keys */ #include "platform.h" #include @@ -33,62 +36,12 @@ #include "taler_json_lib.h" #include "taler-mint-httpd_parsing.h" #include "taler-mint-httpd_keys.h" +#include "taler-mint-httpd_db.h" #include "taler-mint-httpd_mhd.h" #include "taler-mint-httpd_withdraw.h" #include "taler-mint-httpd_responses.h" -/** - * 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 - */ -static json_t * -sig_to_json (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, - const struct GNUNET_CRYPTO_EddsaSignature *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, "sig", el); - - return root; -} - - -/** - * Sign a reserve's status with the current signing key. - * - * @param reserve the reserve to sign - * @param key_state the key state containing the current - * signing private key - */ -static void -sign_reserve (struct Reserve *reserve, - struct MintKeyState *key_state) -{ - reserve->status_sign_pub = key_state->current_sign_key_issue.issue.signkey_pub; - reserve->status_sig_purpose.purpose = htonl (TALER_SIGNATURE_RESERVE_STATUS); - reserve->status_sig_purpose.size = htonl (sizeof (struct Reserve) - - offsetof (struct Reserve, status_sig_purpose)); - GNUNET_CRYPTO_eddsa_sign (&key_state->current_sign_key_issue.signkey_priv, - &reserve->status_sig_purpose, - &reserve->status_sig); -} - - /** * Handle a "/withdraw/status" request * @@ -107,100 +60,18 @@ TALER_MINT_handler_withdraw_status (struct RequestHandler *rh, size_t *upload_data_size) { struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; - PGconn *db_conn; int res; - struct Reserve reserve; - struct MintKeyState *key_state; - int must_update = GNUNET_NO; - json_t *json; res = TALER_MINT_mhd_request_arg_data (connection, - "reserve_pub", - &reserve_pub, - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); + "reserve_pub", + &reserve_pub, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); if (GNUNET_SYSERR == res) - { - // FIXME: return 'internal error' - GNUNET_break (0); - return MHD_NO; - } - if (GNUNET_OK != res) - return MHD_YES; - if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) - { - // FIXME: return 'internal error'? - GNUNET_break (0); - return MHD_NO; - } - res = TALER_MINT_DB_get_reserve (db_conn, - &reserve_pub, - &reserve); - if (GNUNET_SYSERR == res) - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", - "Reserve not found"); - if (GNUNET_OK != res) - { - // FIXME: return 'internal error'? - GNUNET_break (0); - return MHD_NO; - } - key_state = TALER_MINT_key_state_acquire (); - if (0 != memcmp (&key_state->current_sign_key_issue.issue.signkey_pub, - &reserve.status_sign_pub, - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))) - { - sign_reserve (&reserve, key_state); - must_update = GNUNET_YES; - } - if ((GNUNET_YES == must_update) && - (GNUNET_OK != TALER_MINT_DB_update_reserve (db_conn, &reserve, !must_update))) - { - GNUNET_break (0); - return MHD_YES; - } - - /* Convert the public information of a reserve (i.e. - excluding private key) to a JSON object. */ - json = json_object (); - json_object_set_new (json, - "balance", - TALER_JSON_from_amount (TALER_amount_ntoh (reserve.balance))); - json_object_set_new (json, - "expiration", - TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (reserve.expiration))); - json_object_set_new (json, - "signature", - sig_to_json (&reserve.status_sig_purpose, - &reserve.status_sig)); - - return TALER_MINT_reply_json (connection, - json, - MHD_HTTP_OK); -} - - -/** - * Send positive, normal response for "/withdraw/sign". - * - * @param connection the connection to send the response to - * @param collectable the collectable blindcoin (i.e. the blindly signed coin) - * @return a MHD result code - */ -static int -helper_withdraw_sign_send_reply (struct MHD_Connection *connection, - const struct CollectableBlindcoin *collectable) -{ - json_t *root = json_object (); - - json_object_set_new (root, "ev_sig", - TALER_JSON_from_data (&collectable->ev_sig, - sizeof (struct TALER_RSA_Signature))); - return TALER_MINT_reply_json (connection, - root, - MHD_HTTP_OK); + return MHD_NO; /* internal error */ + if (GNUNET_NO == res) + return MHD_YES; /* parse error */ + return TALER_MINT_db_execute_withdraw_status (connection, + &reserve_pub); } @@ -223,180 +94,44 @@ TALER_MINT_handler_withdraw_sign (struct RequestHandler *rh, { struct TALER_WithdrawRequest wsrd; int res; - PGconn *db_conn; - struct Reserve reserve; - struct MintKeyState *key_state; - struct CollectableBlindcoin collectable; - struct TALER_MINT_DenomKeyIssuePriv *dki; - struct TALER_RSA_Signature ev_sig; - struct TALER_Amount amount_required; - memset (&wsrd, - 0, - sizeof (struct TALER_WithdrawRequest)); res = TALER_MINT_mhd_request_arg_data (connection, - "reserve_pub", - &wsrd.reserve_pub, - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); + "reserve_pub", + &wsrd.reserve_pub, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); if (GNUNET_SYSERR == res) - { - // FIXME: return 'internal error'? - GNUNET_break (0); - return MHD_NO; - } - if (GNUNET_OK != res) - return MHD_YES; + return MHD_NO; /* internal error */ + if (GNUNET_NO == res) + return MHD_YES; /* invalid request */ + + /* FIXME: handle variable-size signing keys! */ res = TALER_MINT_mhd_request_arg_data (connection, "denom_pub", &wsrd.denomination_pub, sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)); if (GNUNET_SYSERR == res) - { - // FIXME: return 'internal error'? - GNUNET_break (0); - return MHD_NO; - } - if (GNUNET_OK != res) - return MHD_YES; - res = TALER_MINT_mhd_request_arg_data (connection, - "coin_ev", - &wsrd.coin_envelope, - sizeof (struct TALER_RSA_Signature)); - if (GNUNET_SYSERR == res) - { - // FIXME: return 'internal error'? - GNUNET_break (0); - return MHD_NO; - } - if (GNUNET_OK != res) - return MHD_YES; - res = TALER_MINT_mhd_request_arg_data (connection, - "reserve_sig", - &wsrd.sig, - sizeof (struct GNUNET_CRYPTO_EddsaSignature)); - if (GNUNET_SYSERR == res) - { - // FIXME: return 'internal error'? - GNUNET_break (0); - return MHD_NO; - } - if (GNUNET_OK != res) - return MHD_YES; - - if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) - { - // FIXME: return 'internal error'? - GNUNET_break (0); - return MHD_NO; - } - - res = TALER_MINT_DB_get_collectable_blindcoin (db_conn, - &wsrd.coin_envelope, - &collectable); - if (GNUNET_SYSERR == res) - { - // FIXME: return 'internal error' - GNUNET_break (0); - return MHD_NO; - } - - /* Don't sign again if we have already signed the coin */ - if (GNUNET_YES == res) - return helper_withdraw_sign_send_reply (connection, - &collectable); - GNUNET_assert (GNUNET_NO == res); - res = TALER_MINT_DB_get_reserve (db_conn, - &wsrd.reserve_pub, - &reserve); - if (GNUNET_SYSERR == res) - { - // FIXME: return 'internal error' - GNUNET_break (0); - return MHD_NO; - } + return MHD_NO; /* internal error */ if (GNUNET_NO == res) - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", - "Reserve not found"); + return MHD_YES; /* invalid request */ + res = TALER_MINT_mhd_request_arg_data (connection, + "coin_ev", + &wsrd.coin_envelope, + sizeof (struct TALER_RSA_Signature)); + if (GNUNET_SYSERR == res) + return MHD_NO; /* internal error */ + if (GNUNET_NO == res) + return MHD_YES; /* invalid request */ + res = TALER_MINT_mhd_request_arg_data (connection, + "reserve_sig", + &wsrd.sig, + sizeof (struct GNUNET_CRYPTO_EddsaSignature)); + if (GNUNET_SYSERR == res) + return MHD_NO; /* internal error */ + if (GNUNET_NO == res) + return MHD_YES; /* invalid request */ - // fill out all the missing info in the request before - // we can check the signature on the request - - wsrd.purpose.purpose = htonl (TALER_SIGNATURE_WITHDRAW); - wsrd.purpose.size = htonl (sizeof (struct TALER_WithdrawRequest) - - offsetof (struct TALER_WithdrawRequest, purpose)); - - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WITHDRAW, - &wsrd.purpose, - &wsrd.sig, - &wsrd.reserve_pub)) - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_UNAUTHORIZED, - "{s:s}", - "error", "Invalid Signature"); - - key_state = TALER_MINT_key_state_acquire (); - dki = TALER_MINT_get_denom_key (key_state, - &wsrd.denomination_pub); - TALER_MINT_key_state_release (key_state); - if (NULL == dki) - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", - "Denomination not found"); - - amount_required = TALER_amount_ntoh (dki->issue.value); - amount_required = TALER_amount_add (amount_required, - TALER_amount_ntoh (dki->issue.fee_withdraw)); - - if (0 < TALER_amount_cmp (amount_required, - TALER_amount_ntoh (reserve.balance))) - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_PAYMENT_REQUIRED, - "{s:s}", - "error", - "Insufficient funds"); - if (GNUNET_OK != - TALER_RSA_sign (dki->denom_priv, - &wsrd.coin_envelope, - sizeof (struct TALER_RSA_BlindedSignaturePurpose), - &ev_sig)) - { - // FIXME: return 'internal error' - GNUNET_break (0); - return MHD_NO; - } - - reserve.balance = TALER_amount_hton (TALER_amount_subtract (TALER_amount_ntoh (reserve.balance), - amount_required)); - if (GNUNET_OK != - TALER_MINT_DB_update_reserve (db_conn, - &reserve, - GNUNET_YES)) - { - // FIXME: return 'internal error' - GNUNET_break (0); - return MHD_NO; - } - - collectable.ev = wsrd.coin_envelope; - collectable.ev_sig = ev_sig; - collectable.reserve_pub = wsrd.reserve_pub; - collectable.reserve_sig = wsrd.sig; - if (GNUNET_OK != - TALER_MINT_DB_insert_collectable_blindcoin (db_conn, - &collectable)) - { - // FIXME: return 'internal error' - GNUNET_break (0); - return GNUNET_NO;; - } - return helper_withdraw_sign_send_reply (connection, - &collectable); + return TALER_MINT_db_execute_withdraw_sign (connection, + &wsrd); } /* end of taler-mint-httpd_withdraw.c */ diff --git a/src/util/json.c b/src/util/json.c index 269e6cf26..d66c95c43 100644 --- a/src/util/json.c +++ b/src/util/json.c @@ -90,6 +90,34 @@ TALER_JSON_from_abs (struct GNUNET_TIME_Absolute stamp) } +/** + * 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_sig (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + const struct GNUNET_CRYPTO_EddsaSignature *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, "sig", el); + + return root; +} + /** * Convert binary data to a JSON string