diff options
author | Christian Grothoff <christian@grothoff.org> | 2015-03-10 17:00:12 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2015-03-10 17:00:12 +0100 |
commit | 7e26ae385896a974d8a90234a155c32b383e70f7 (patch) | |
tree | 1bbcf3e317744c9fa024bba5a6e46c84520dc9e5 /src | |
parent | e3b8f5117e8028f3183871ab0efb0c6f2ff959eb (diff) |
clean up refresh logic and use of signatures
Diffstat (limited to 'src')
-rw-r--r-- | src/include/taler_signatures.h | 48 | ||||
-rw-r--r-- | src/mint/taler-mint-httpd_refresh.c | 293 | ||||
-rw-r--r-- | src/mint/taler-mint-httpd_responses.c | 7 |
3 files changed, 145 insertions, 203 deletions
diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index 9e3068a78..b1b578236 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -65,7 +65,7 @@ * Signature where the refresh session confirms * the commits. */ -#define TALER_SIGNATURE_REFRESH_MELT 6 +#define TALER_SIGNATURE_REFRESH_MELT_SESSION 6 /** * Signature where the mint (current signing key) @@ -221,7 +221,7 @@ struct TALER_DepositConfirmation struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub; /** - * The Merachant's public key. + * The Merchant's public key. */ struct GNUNET_CRYPTO_EddsaPublicKey merchant; @@ -232,7 +232,7 @@ struct TALER_DepositConfirmation * Message signed by a coin to indicate that the coin should * be melted. */ -struct RefreshMeltSignatureBody +struct RefreshMeltCoinSignature { /** * Purpose is #TALER_SIGNATURE_REFRESH_MELT_COIN. @@ -245,42 +245,46 @@ struct RefreshMeltSignatureBody struct GNUNET_HashCode melt_hash; /** - * Signature of the client over the melt request (thereby - * indirectly including all of the information the client - * sent). - */ - struct GNUNET_CRYPTO_EddsaSignature melt_client_signature; - - /** - * Public key of the refresh session for which - * @e melt_client_signature must be a valid signature. - */ - struct GNUNET_CRYPTO_EddsaPublicKey session_key; - - /** * How much of the value of the coin should be melted? * This amount includes the fees, so the final amount contributed * to the melt is this value minus the fee for melting the coin. */ struct TALER_AmountNBO amount; + + /** + * The coin's public key. + */ + struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub; }; /** - * Message signed during melting committing the client to the - * hashed inputs. + * Message signed by a coin to indicate that the coin should + * be melted. */ -struct RefreshCommitSignatureBody +struct RefreshMeltSessionSignature { /** - * Purpose is #TALER_SIGNATURE_REFRESH_MELT. + * Purpose is #TALER_SIGNATURE_REFRESH_MELT_SESSION */ struct GNUNET_CRYPTO_EccSignaturePurpose purpose; /** - * Session state the client commits itself to. + * Which melting operation should the coin become a part of. + */ + struct GNUNET_HashCode melt_hash; + + /** + * Public key of the refresh session for which + * @e melt_client_signature must be a valid signature. */ - struct GNUNET_HashCode commit_hash; + struct GNUNET_CRYPTO_EddsaPublicKey session_key; + + /** + * What is the total value of the coins created during the + * refresh, excluding fees? + */ + struct TALER_AmountNBO amount; }; diff --git a/src/mint/taler-mint-httpd_refresh.c b/src/mint/taler-mint-httpd_refresh.c index 6c3cbbc71..d36e42085 100644 --- a/src/mint/taler-mint-httpd_refresh.c +++ b/src/mint/taler-mint-httpd_refresh.c @@ -36,109 +36,6 @@ /** - * Verify a signature that is encoded in a JSON object. Extracts - * the signature and its associated purpose and checks that it - * matches the specified @a purpose and @a pub public key. Any - * errors are reported via appropriate response messages. - * - * @param connection the connection to send errors to - * @param json_sig the JSON object with the signature - * @param the public key that the signature was created with - * @param purpose the signed message - * @return #GNUNET_YES if the signature was valid - * #GNUNET_NO if the signature was invalid - * #GNUNET_SYSERR on internal error - */ -static int -request_json_check_signature (struct MHD_Connection *connection, - const json_t *json_sig, - const struct GNUNET_CRYPTO_EddsaPublicKey *pub, - const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose) -{ - struct GNUNET_CRYPTO_EddsaSignature signature; - int size; - uint32_t purpose_num; - int res; - json_t *el; - - /* TODO: use specification array to simplify the parsing! */ - res = GNUNET_MINT_parse_navigate_json (connection, - json_sig, - JNAV_FIELD, - "sig", - JNAV_RET_DATA, - &signature, - sizeof (struct GNUNET_CRYPTO_EddsaSignature)); - - if (GNUNET_OK != res) - return res; - - res = GNUNET_MINT_parse_navigate_json (connection, - json_sig, - JNAV_FIELD, - "purpose", - JNAV_RET_TYPED_JSON, - JSON_INTEGER, - &el); - - if (GNUNET_OK != res) - return res; - - purpose_num = json_integer_value (el); - - if (purpose_num != ntohl (purpose->purpose)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "signature invalid (purpose wrong)\n"); - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s}", - "error", "signature invalid (purpose)"); - } - - res = GNUNET_MINT_parse_navigate_json (connection, - json_sig, - JNAV_FIELD, "size", - JNAV_RET_TYPED_JSON, - JSON_INTEGER, - &el); - - if (GNUNET_OK != res) - return res; - - size = json_integer_value (el); - - if (size != ntohl (purpose->size)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "signature invalid (size wrong)\n"); - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - GNUNET_NO, GNUNET_SYSERR, - "{s:s}", - "error", - "signature invalid (size)"); - } - - if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (purpose_num, - purpose, - &signature, - pub)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "signature invalid (did not verify)\n"); - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_UNAUTHORIZED, - "{s:s}", - "error", - "invalid signature (verification)"); - } - - return GNUNET_OK; -} - - -/** * Handle a "/refresh/melt" request after the main JSON parsing has happened. * We now need to validate the coins being melted and the session signature * and then hand things of to execute the melt operation. @@ -150,7 +47,7 @@ request_json_check_signature (struct MHD_Connection *connection, * @param coin_count number of coins to be melted, size of y-dimension of @commit_coin array * @param coin_public_infos array with @a coin_count entries about the coins * @param coin_melt_details array with @a coin_count entries with melting details - * @param melt_sig_json signature affirming the overall melt operation + * @param commit_hash hash over the data that the client commits to * @param commit_client_sig signature of the client over this commitment * @param kappa size of x-dimension of @commit_coin and @commit_link arrays * @param commit_coin 2d array of coin commitments (what the mint is to sign @@ -168,18 +65,17 @@ handle_refresh_melt_binary (struct MHD_Connection *connection, unsigned int coin_count, struct TALER_CoinPublicInfo *coin_public_infos, const struct MeltDetails *coin_melt_details, - const json_t *melt_sig_json, + const struct GNUNET_HashCode *commit_hash, const struct GNUNET_CRYPTO_EddsaSignature *commit_client_sig, unsigned int kappa, struct RefreshCommitCoin *const* commit_coin, struct RefreshCommitLink *const* commit_link) { - int res; unsigned int i; struct GNUNET_HashContext *hash_context; struct GNUNET_HashCode melt_hash; - struct RefreshMeltSignatureBody body; + struct RefreshMeltSessionSignature body; char *buf; size_t buf_size; struct MintKeyState *key_state; @@ -207,18 +103,26 @@ handle_refresh_melt_binary (struct MHD_Connection *connection, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); GNUNET_CRYPTO_hash_context_finish (hash_context, &melt_hash); + // FIXME: what about the `commit_hash`? - body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT); - body.purpose.size = htonl (sizeof (struct RefreshMeltSignatureBody)); + body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_SESSION); + body.purpose.size = htonl (sizeof (struct RefreshMeltSessionSignature)); body.melt_hash = melt_hash; body.amount = TALER_amount_hton (coin_melt_details->melt_amount); - if (GNUNET_OK != - (res = request_json_check_signature (connection, - melt_sig_json, - refresh_session_pub, - &body.purpose))) - return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; + if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_REFRESH_MELT_SESSION, + &body.purpose, + commit_client_sig, + refresh_session_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "signature invalid (did not verify)\n"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_UNAUTHORIZED, + "{s:s}", + "error", + "invalid signature (verification)"); + } // FIXME: badness, use proper way to set to zero... key_state = TALER_MINT_key_state_acquire (); @@ -276,15 +180,9 @@ handle_refresh_melt_binary (struct MHD_Connection *connection, /** - * Extract public coin information from a JSON object and verify - * that the signature shows that this coin is to be melted into - * the given @a session_pub melting session, and that this is - * a valid coin (we know the denomination key and the signature - * on it is valid). Essentially, this does all of the per-coin - * checks that can be done before the transaction starts. + * Extract public coin information from a JSON object. * * @param connection the connection to send error responses to - * @param session_pub public key of the session the coin is melted into * @param coin_info the JSON object to extract the coin info from * @param r_public_info[OUT] set to the coin's public information * @param r_melt_detail[OUT] set to details about the coin's melting permission (if valid) @@ -293,19 +191,15 @@ handle_refresh_melt_binary (struct MHD_Connection *connection, * #GNUNET_SYSERR on internal error */ static int -get_and_verify_coin_public_info (struct MHD_Connection *connection, - const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, - json_t *coin_info, - struct TALER_CoinPublicInfo *r_public_info, - struct MeltDetails *r_melt_detail) +get_coin_public_info (struct MHD_Connection *connection, + json_t *coin_info, + struct TALER_CoinPublicInfo *r_public_info, + struct MeltDetails *r_melt_detail) { int ret; struct GNUNET_CRYPTO_EcdsaSignature melt_sig; struct GNUNET_CRYPTO_rsa_Signature *sig; struct GNUNET_CRYPTO_rsa_PublicKey *pk; - struct RefreshMeltConfirmSignRequestBody body; - struct MintKeyState *key_state; - struct TALER_MINT_DenomKeyIssuePriv *dki; struct TALER_Amount amount; struct GNUNET_MINT_ParseFieldSpec spec[] = { TALER_MINT_PARSE_FIXED ("coin_pub", &r_public_info->coin_pub), @@ -316,6 +210,7 @@ get_and_verify_coin_public_info (struct MHD_Connection *connection, TALER_MINT_PARSE_END }; + memset (&amount, 0, sizeof (amount)); // FIXME: #3636! ret = TALER_MINT_parse_json_data (connection, coin_info, spec); @@ -323,17 +218,68 @@ get_and_verify_coin_public_info (struct MHD_Connection *connection, return ret; /* FIXME: include amount of coin value to be melted here (#3636!) and in what we return!? */ - memset (&amount, 0, sizeof (amount)); // FIXME: #3636! - body.purpose.size = htonl (sizeof (struct RefreshMeltConfirmSignRequestBody)); - body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_CONFIRM); - body.session_pub = *session_pub; + + /* check mint signature on the coin */ + r_public_info->denom_sig = sig; + r_public_info->denom_pub = pk; if (GNUNET_OK != - GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_REFRESH_MELT_CONFIRM, + TALER_test_coin_valid (r_public_info)) + { + TALER_MINT_release_parsed_data (spec); + r_public_info->denom_sig = NULL; + r_public_info->denom_pub = NULL; + return (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", "coin invalid")) + ? GNUNET_NO : GNUNET_SYSERR; + } + r_melt_detail->melt_sig = melt_sig; + r_melt_detail->melt_amount = amount; + TALER_MINT_release_parsed_data (spec); + return GNUNET_OK; +} + + +/** + * Verify that the signature shows that this coin is to be melted into + * the given @a session_pub melting session, and that this is a valid + * coin (we know the denomination key and the signature on it is + * valid). Essentially, this does all of the per-coin checks that can + * be done before the transaction starts. + * + * @param connection the connection to send error responses to + * @param melt_hash hash over refresh session the coin is melted into + * @param r_public_info the coin's public information + * @param r_melt_detail details about the coin's melting permission (if valid) + * @return #GNUNET_YES if coin public info in JSON was valid + * #GNUNET_NO JSON was invalid, response was generated + * #GNUNET_SYSERR on internal error + */ +static int +verify_coin_public_info (struct MHD_Connection *connection, + const struct GNUNET_HashCode *melt_hash, + const struct TALER_CoinPublicInfo *r_public_info, + const struct MeltDetails *r_melt_detail) +{ + struct RefreshMeltCoinSignature body; + struct MintKeyState *key_state; + struct TALER_MINT_DenomKeyIssuePriv *dki; + + /* FIXME: include amount of coin value to be melted here (#3636!) and + in what we return!? */ + body.purpose.size = htonl (sizeof (struct RefreshMeltCoinSignature)); + body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_COIN); + body.melt_hash = *melt_hash; + body.amount = TALER_amount_hton (r_melt_detail->melt_amount); + body.coin_pub = r_public_info->coin_pub; + if (GNUNET_OK != + GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_REFRESH_MELT_COIN, &body.purpose, - &melt_sig, + &r_melt_detail->melt_sig, &r_public_info->coin_pub)) { - TALER_MINT_release_parsed_data (spec); if (MHD_YES != TALER_MINT_reply_json_pack (connection, MHD_HTTP_UNAUTHORIZED, @@ -344,7 +290,7 @@ get_and_verify_coin_public_info (struct MHD_Connection *connection, } key_state = TALER_MINT_key_state_acquire (); dki = TALER_MINT_get_denom_key (key_state, - pk); + r_public_info->denom_pub); /* FIXME: need to check if denomination key is still valid for issuing! (#3634) */ if (NULL == dki) @@ -355,25 +301,6 @@ get_and_verify_coin_public_info (struct MHD_Connection *connection, "denom_pub"); } TALER_MINT_key_state_release (key_state); - - /* check mint signature on the coin */ - r_public_info->denom_sig = sig; - r_public_info->denom_pub = pk; - if (GNUNET_OK != - TALER_test_coin_valid (r_public_info)) - { - TALER_MINT_release_parsed_data (spec); - r_public_info->denom_sig = NULL; - r_public_info->denom_pub = NULL; - return (MHD_YES == - TALER_MINT_reply_json_pack (connection, - MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", "coin invalid")) - ? GNUNET_NO : GNUNET_SYSERR; - } - r_melt_detail->melt_sig = melt_sig; - r_melt_detail->melt_amount = amount; return GNUNET_OK; } @@ -478,9 +405,9 @@ handle_refresh_melt_json (struct MHD_Connection *connection, unsigned int coin_count; struct GNUNET_HashCode commit_hash; struct GNUNET_HashContext *hash_context; - struct RefreshCommitSignatureBody body; struct RefreshCommitCoin *commit_coin[kappa]; struct RefreshCommitLink *commit_link[kappa]; + const struct GNUNET_CRYPTO_EddsaSignature commit_client_sig; num_new_denoms = json_array_size (new_denoms); denom_pubs = GNUNET_malloc (num_new_denoms * @@ -509,11 +436,10 @@ handle_refresh_melt_json (struct MHD_Connection *connection, for (i=0;i<coin_count;i++) { /* decode JSON data on coin to melt */ - res = get_and_verify_coin_public_info (connection, - refresh_session_pub, - json_array_get (melt_coins, i), - &coin_public_infos[i], - &coin_melt_details[i]); + res = get_coin_public_info (connection, + json_array_get (melt_coins, i), + &coin_public_infos[i], + &coin_melt_details[i]); if (GNUNET_OK != res) { for (j=0;j<i;j++) @@ -635,24 +561,34 @@ handle_refresh_melt_json (struct MHD_Connection *connection, } GNUNET_CRYPTO_hash_context_finish (hash_context, &commit_hash); - /* verify commit signature */ - body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT); - body.purpose.size = htonl (sizeof (struct RefreshCommitSignatureBody)); - body.commit_hash = commit_hash; - if (GNUNET_OK != - (res = request_json_check_signature (connection, - commit_signature, - refresh_session_pub, - &body.purpose))) + res = GNUNET_MINT_parse_navigate_json (connection, + commit_signature, + JNAV_FIELD, + "sig", + JNAV_RET_DATA, + &commit_client_sig, + sizeof (struct GNUNET_CRYPTO_EddsaSignature)); + + if (GNUNET_OK != res) + return (GNUNET_NO == res) ? MHD_YES : MHD_NO; + + + for (i=0;i<coin_count;i++) { - free_commit_coins (commit_coin, kappa, num_newcoins); - free_commit_links (commit_link, kappa, num_oldcoins); - return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; + /* verify signatures ons coin to melt */ + res = verify_coin_public_info (connection, + &commit_hash, + &coin_public_infos[i], + &coin_melt_details[i]); + if (GNUNET_OK != res) + { + res = (GNUNET_NO == res) ? MHD_YES : MHD_NO; + goto cleanup; + } } /* execute commit */ - /* FIXME: we must also store the signature! (#3635) */ res = handle_refresh_melt_binary (connection, refresh_session_pub, num_new_denoms, @@ -660,11 +596,12 @@ handle_refresh_melt_json (struct MHD_Connection *connection, coin_count, coin_public_infos, coin_melt_details, - melt_sig_json, - NULL /* FIXME: 3635! */, + &commit_hash, + &commit_client_sig, kappa, commit_coin, commit_link); + cleanup: free_commit_coins (commit_coin, kappa, num_newcoins); free_commit_links (commit_link, kappa, num_oldcoins); for (j=0;j<coin_count;j++) diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index 83c81e800..2faeb5ca8 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -355,15 +355,16 @@ compile_transaction_history (const struct TALER_MINT_DB_TransactionList *tl) } case TALER_MINT_DB_TT_REFRESH_MELT: { - struct RefreshMeltSignatureBody ms; + struct RefreshMeltCoinSignature ms; const struct RefreshMelt *melt = pos->details.melt; type = "melt"; value = melt->amount; - ms.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT); - ms.purpose.size = htonl (sizeof (struct RefreshMeltSignatureBody)); + ms.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_COIN); + ms.purpose.size = htonl (sizeof (struct RefreshMeltCoinSignature)); ms.melt_hash = melt->melt_hash; ms.amount = TALER_amount_hton (melt->amount); + ms.coin_pub = melt->coin.coin_pub; transaction = TALER_JSON_from_ecdsa_sig (&ms.purpose, &melt->coin_sig); } |