more code refactoring to separate parsing, db and response generation nicely

This commit is contained in:
Christian Grothoff 2015-01-19 21:53:23 +01:00
parent f9347d2395
commit ed51946442
13 changed files with 652 additions and 533 deletions

View File

@ -27,8 +27,7 @@
/** /**
* Convert a TALER amount to a JSON * Convert a TALER amount to a JSON object.
* object.
* *
* @param amount the amount * @param amount the amount
* @return a json object describing the amount * @return a json object describing the amount
@ -47,6 +46,17 @@ json_t *
TALER_JSON_from_abs (struct GNUNET_TIME_Absolute stamp); 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 * 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 json the json object representing Amount
* @param r_amount where the amount has to be written * @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 int
TALER_JSON_to_amount (json_t *json, 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 json the json object representing absolute time in seconds
* @param r_abs where the time has to be written * @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 int
TALER_JSON_to_abs (json_t *json, 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 json the json object representing data
* @param out the pointer to hold the parsed data. * @param out the pointer to hold the parsed data.
* @param out_size the size of r_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 int
TALER_JSON_to_data (json_t *json, TALER_JSON_to_data (json_t *json,

View File

@ -13,14 +13,15 @@
You should have received a copy of the GNU General Public License along with You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/ */
/** /**
* @file taler_mint.h * @file mint.h
* @brief Common functionality for the mint * @brief Common functionality for the mint
* @author Florian Dold * @author Florian Dold
* @author Benedikt Mueller * @author Benedikt Mueller
*
* TODO:
* - revisit and document `struct Deposit` members.
*/ */
#ifndef _MINT_H #ifndef _MINT_H
#define _MINT_H #define _MINT_H
@ -55,11 +56,195 @@ struct TALER_MINT_DenomKeyIssuePriv
* not available. * not available.
*/ */
struct TALER_RSA_PrivateKey *denom_priv; struct TALER_RSA_PrivateKey *denom_priv;
struct TALER_MINT_DenomKeyIssue issue; 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); int indices[3], struct TALER_AmountNBO *denom_nbo);
#endif /* _MINT_H */ #endif /* _MINT_H */

View File

@ -29,149 +29,8 @@
#include "taler_util.h" #include "taler_util.h"
#include "taler_rsa.h" #include "taler_rsa.h"
#include "taler-mint-httpd_db.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 int
TALER_MINT_DB_prepare (PGconn *db_conn); TALER_MINT_DB_prepare (PGconn *db_conn);

View File

@ -28,7 +28,6 @@
#include <libpq-fe.h> #include <libpq-fe.h>
#include <pthread.h> #include <pthread.h>
#include "mint.h" #include "mint.h"
#include "mint_db.h"
#include "taler_signatures.h" #include "taler_signatures.h"
#include "taler_rsa.h" #include "taler_rsa.h"
#include "taler_json_lib.h" #include "taler_json_lib.h"
@ -38,6 +37,7 @@
#include "taler-mint-httpd_deposit.h" #include "taler-mint-httpd_deposit.h"
#include "taler-mint-httpd_withdraw.h" #include "taler-mint-httpd_withdraw.h"
#include "taler-mint-httpd_refresh.h" #include "taler-mint-httpd_refresh.h"
#include "mint_db.h"
/** /**

View File

@ -25,12 +25,15 @@
* - /deposit: check for leaks * - /deposit: check for leaks
*/ */
#include "platform.h" #include "platform.h"
#include <pthread.h>
#include <jansson.h>
#include "taler-mint-httpd_db.h" #include "taler-mint-httpd_db.h"
#include "taler_signatures.h" #include "taler_signatures.h"
#include "taler-mint-httpd_keys.h"
#include "taler-mint-httpd_responses.h" #include "taler-mint-httpd_responses.h"
#include "mint_db.h" #include "mint_db.h"
#include "mint.h" #include "mint.h"
#include <pthread.h> #include "taler_json_lib.h"
/** /**
@ -48,43 +51,39 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,
const struct Deposit *deposit) const struct Deposit *deposit)
{ {
PGconn *db_conn; PGconn *db_conn;
struct Deposit *existing_deposit;
int res;
if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
{ {
GNUNET_break (0); GNUNET_break (0);
return TALER_MINT_reply_internal_error (connection, 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); GNUNET_break (0);
/* FIXME: return error message to client via MHD! */ /* FIXME: return error message to client via MHD! */
return MHD_NO; return MHD_NO;
}
} }
{ {
@ -133,3 +132,228 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection,
TALER_MINT_DB_commit (db_conn); TALER_MINT_DB_commit (db_conn);
return TALER_MINT_reply_deposit_success (connection, deposit); 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);
}

View File

@ -17,9 +17,6 @@
* @file mint/taler-mint_httpd_db.h * @file mint/taler-mint_httpd_db.h
* @brief Mint-specific database access * @brief Mint-specific database access
* @author Chrisitan Grothoff * @author Chrisitan Grothoff
*
* TODO:
* - revisit and document `struct Deposit` members.
*/ */
#ifndef TALER_MINT_HTTPD_DB_H #ifndef TALER_MINT_HTTPD_DB_H
#define TALER_MINT_HTTPD_DB_H #define TALER_MINT_HTTPD_DB_H
@ -29,46 +26,13 @@
#include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_util_lib.h>
#include "taler_util.h" #include "taler_util.h"
#include "taler_rsa.h" #include "taler_rsa.h"
#include "taler-mint-httpd_keys.h"
#include "mint.h"
/** /**
* Specification for a /deposit operation. * Execute a /deposit. The validity of the coin and signature
*/
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
* have already been checked. The database must now check that * have already been checked. The database must now check that
* the coin is not (double or over) spent, and execute the * the coin is not (double or over) spent, and execute the
* transaction (record details, generate success or failure response). * 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); 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 */ #endif /* _NEURO_MINT_DB_H */

View File

@ -27,7 +27,6 @@
#include <libpq-fe.h> #include <libpq-fe.h>
#include <pthread.h> #include <pthread.h>
#include "mint.h" #include "mint.h"
#include "mint_db.h"
#include "taler_signatures.h" #include "taler_signatures.h"
#include "taler_rsa.h" #include "taler_rsa.h"
#include "taler_json_lib.h" #include "taler_json_lib.h"
@ -35,6 +34,7 @@
#include "taler-mint-httpd_keys.h" #include "taler-mint-httpd_keys.h"
/** /**
* Mint key state. Never use directly, instead access via * Mint key state. Never use directly, instead access via
* #TALER_MINT_key_state_acquire and #TALER_MINT_key_state_release. * #TALER_MINT_key_state_acquire and #TALER_MINT_key_state_release.

View File

@ -26,7 +26,7 @@
#include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_util_lib.h>
#include <microhttpd.h> #include <microhttpd.h>
#include "taler-mint-httpd.h" #include "taler-mint-httpd.h"
#include "mint.h"
/** /**
* Snapshot of the (coin and signing) * Snapshot of the (coin and signing)

View File

@ -19,6 +19,9 @@
* @author Florian Dold * @author Florian Dold
* @author Benedikt Mueller * @author Benedikt Mueller
* @author Christian Grothoff * @author Christian Grothoff
*
* TODO:
* - split properly into parsing, DB-ops and response generation
*/ */
#include "platform.h" #include "platform.h"
#include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_util_lib.h>
@ -70,6 +73,9 @@ sign_as_json (struct GNUNET_CRYPTO_EccSignaturePurpose *purpose)
} }
/**
* FIXME: document!
*/
static int static int
link_iter (void *cls, link_iter (void *cls,
const struct LinkDataEnc *link_data_enc, 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 connection the connection to send error responses to
* @param root the JSON object to extract the coin info from * @param root the JSON object to extract the coin info from
* @return GNUNET_YES if coin public info in JSON was valid * @return #GNUNET_YES if coin public info in JSON was valid
* GNUNET_NO otherwise * #GNUNET_NO otherwise
* GNUNET_SYSERR on internal error * #GNUNET_SYSERR on internal error
*/ */
static int static int
request_json_require_coin_public_info (struct MHD_Connection *connection, 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 root the JSON object
* @param hash_context the hash context that will receive * @param hash_context the hash context that will receive
* the coin public keys of the melted coin * the coin public keys of the melted coin
* @return a GNUnet result code, GNUNET_OK on success, * @return #GNUNET_OK on success,
* GNUNET_NO if an error message was generated, * #GNUNET_NO if an error message was generated,
* GNUNET_SYSERR on internal errors (no response generated) * #GNUNET_SYSERR on internal errors (no response generated)
*/ */
static int static int
refresh_accept_melts (struct MHD_Connection *connection, refresh_accept_melts (struct MHD_Connection *connection,

View File

@ -26,9 +26,14 @@
* TODO: * TODO:
* - when generating /deposit reply, do include signature of mint * - when generating /deposit reply, do include signature of mint
* to say that we accepted it (check reply format) * 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 "platform.h"
#include "taler-mint-httpd_responses.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"); "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 */ /* end of taler-mint-httpd_responses.c */

View File

@ -31,8 +31,8 @@
#include <libpq-fe.h> #include <libpq-fe.h>
#include <pthread.h> #include <pthread.h>
#include "taler-mint-httpd.h" #include "taler-mint-httpd.h"
#include "taler-mint-httpd_db.h"
#include "taler-mint-httpd_mhd.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); 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 #endif

View File

@ -19,6 +19,9 @@
* @author Florian Dold * @author Florian Dold
* @author Benedikt Mueller * @author Benedikt Mueller
* @author Christian Grothoff * @author Christian Grothoff
*
* TODO:
* - support variable-size RSA keys
*/ */
#include "platform.h" #include "platform.h"
#include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_util_lib.h>
@ -33,62 +36,12 @@
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler-mint-httpd_parsing.h" #include "taler-mint-httpd_parsing.h"
#include "taler-mint-httpd_keys.h" #include "taler-mint-httpd_keys.h"
#include "taler-mint-httpd_db.h"
#include "taler-mint-httpd_mhd.h" #include "taler-mint-httpd_mhd.h"
#include "taler-mint-httpd_withdraw.h" #include "taler-mint-httpd_withdraw.h"
#include "taler-mint-httpd_responses.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 * Handle a "/withdraw/status" request
* *
@ -107,100 +60,18 @@ TALER_MINT_handler_withdraw_status (struct RequestHandler *rh,
size_t *upload_data_size) size_t *upload_data_size)
{ {
struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
PGconn *db_conn;
int res; 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, res = TALER_MINT_mhd_request_arg_data (connection,
"reserve_pub", "reserve_pub",
&reserve_pub, &reserve_pub,
sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
if (GNUNET_SYSERR == res) if (GNUNET_SYSERR == res)
{ return MHD_NO; /* internal error */
// FIXME: return 'internal error' if (GNUNET_NO == res)
GNUNET_break (0); return MHD_YES; /* parse error */
return MHD_NO; return TALER_MINT_db_execute_withdraw_status (connection,
} &reserve_pub);
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);
} }
@ -223,180 +94,44 @@ TALER_MINT_handler_withdraw_sign (struct RequestHandler *rh,
{ {
struct TALER_WithdrawRequest wsrd; struct TALER_WithdrawRequest wsrd;
int res; 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, res = TALER_MINT_mhd_request_arg_data (connection,
"reserve_pub", "reserve_pub",
&wsrd.reserve_pub, &wsrd.reserve_pub,
sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
if (GNUNET_SYSERR == res) if (GNUNET_SYSERR == res)
{ return MHD_NO; /* internal error */
// FIXME: return 'internal error'? if (GNUNET_NO == res)
GNUNET_break (0); return MHD_YES; /* invalid request */
return MHD_NO;
} /* FIXME: handle variable-size signing keys! */
if (GNUNET_OK != res)
return MHD_YES;
res = TALER_MINT_mhd_request_arg_data (connection, res = TALER_MINT_mhd_request_arg_data (connection,
"denom_pub", "denom_pub",
&wsrd.denomination_pub, &wsrd.denomination_pub,
sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)); sizeof (struct TALER_RSA_PublicKeyBinaryEncoded));
if (GNUNET_SYSERR == res) if (GNUNET_SYSERR == res)
{ return MHD_NO; /* internal error */
// 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;
}
if (GNUNET_NO == res) if (GNUNET_NO == res)
return TALER_MINT_reply_json_pack (connection, return MHD_YES; /* invalid request */
MHD_HTTP_NOT_FOUND, res = TALER_MINT_mhd_request_arg_data (connection,
"{s:s}", "coin_ev",
"error", &wsrd.coin_envelope,
"Reserve not found"); 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 return TALER_MINT_db_execute_withdraw_sign (connection,
// we can check the signature on the request &wsrd);
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);
} }
/* end of taler-mint-httpd_withdraw.c */ /* end of taler-mint-httpd_withdraw.c */

View File

@ -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 * Convert binary data to a JSON string