diff options
Diffstat (limited to 'src/mint/taler-mint-httpd_refresh.c')
-rw-r--r-- | src/mint/taler-mint-httpd_refresh.c | 460 |
1 files changed, 241 insertions, 219 deletions
diff --git a/src/mint/taler-mint-httpd_refresh.c b/src/mint/taler-mint-httpd_refresh.c index 198ad423e..0abc82eb7 100644 --- a/src/mint/taler-mint-httpd_refresh.c +++ b/src/mint/taler-mint-httpd_refresh.c @@ -43,93 +43,14 @@ #include "taler-mint-httpd_keystate.h" -static int -check_confirm_signature (struct MHD_Connection *connection, - json_t *coin_info, - const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, - const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub) -{ - struct RefreshMeltConfirmSignRequestBody body; - struct GNUNET_CRYPTO_EcdsaSignature sig; - int res; - - body.purpose.size = htonl (sizeof (struct RefreshMeltConfirmSignRequestBody)); - body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_CONFIRM); - body.session_pub = *session_pub; - - res = GNUNET_MINT_parse_navigate_json (connection, coin_info, - JNAV_FIELD, "confirm_sig", - JNAV_RET_DATA, - &sig, - sizeof (struct GNUNET_CRYPTO_EcdsaSignature)); - - if (GNUNET_OK != res) - { - GNUNET_break (GNUNET_SYSERR != res); - return res; - } - - if (GNUNET_OK != - GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_REFRESH_MELT_CONFIRM, - &body.purpose, - &sig, - coin_pub)) - { - if (MHD_YES != - TALER_MINT_reply_json_pack (connection, - MHD_HTTP_UNAUTHORIZED, - "{s:s}", - "error", "signature invalid")) - return GNUNET_SYSERR; - return GNUNET_NO; - } - - return GNUNET_OK; -} - - /** - * Extract public coin information from a JSON object. - * - * @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 JSON was invalid, response was generated - * #GNUNET_SYSERR on internal error - */ -static int -request_json_require_coin_public_info (struct MHD_Connection *connection, - json_t *root, - struct TALER_CoinPublicInfo *r_public_info) -{ - int ret; - struct GNUNET_CRYPTO_rsa_Signature *sig; - struct GNUNET_CRYPTO_rsa_PublicKey *pk; - struct GNUNET_MINT_ParseFieldSpec spec[] = - { - TALER_MINT_PARSE_FIXED ("coin_pub", &r_public_info->coin_pub), - TALER_MINT_PARSE_RSA_SIGNATURE ("denom_sig", &sig), - TALER_MINT_PARSE_RSA_PUBLIC_KEY ("denom_pub", &pk), - TALER_MINT_PARSE_END - }; - - ret = TALER_MINT_parse_json_data (connection, - root, - spec); - if (GNUNET_OK != ret) - return ret; - // TALER_MINT_release_parsed_data (spec); - r_public_info->denom_sig = sig; - r_public_info->denom_pub = pk; - return GNUNET_OK; -} - - -/** - * Verify a signature that is encoded in a JSON object + * 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 root the JSON object with the signature + * @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 @@ -138,9 +59,9 @@ request_json_require_coin_public_info (struct MHD_Connection *connection, */ static int request_json_check_signature (struct MHD_Connection *connection, - const json_t *root, + const json_t *json_sig, const struct GNUNET_CRYPTO_EddsaPublicKey *pub, - struct GNUNET_CRYPTO_EccSignaturePurpose *purpose) + const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose) { struct GNUNET_CRYPTO_EddsaSignature signature; int size; @@ -148,20 +69,25 @@ request_json_check_signature (struct MHD_Connection *connection, int res; json_t *el; - res = GNUNET_MINT_parse_navigate_json (connection, root, - JNAV_FIELD, "sig", - JNAV_RET_DATA, - &signature, - sizeof (struct GNUNET_CRYPTO_EddsaSignature)); + /* 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, root, - JNAV_FIELD, "purpose", - JNAV_RET_TYPED_JSON, - JSON_INTEGER, - &el); + 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; @@ -170,18 +96,20 @@ request_json_check_signature (struct MHD_Connection *connection, if (purpose_num != ntohl (purpose->purpose)) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "signature invalid (purpose wrong)\n"); + 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, root, - JNAV_FIELD, "size", - JNAV_RET_TYPED_JSON, - JSON_INTEGER, - &el); + 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; @@ -190,12 +118,14 @@ request_json_check_signature (struct MHD_Connection *connection, if (size != ntohl (purpose->size)) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "signature invalid (size wrong)\n"); + 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)"); + "error", + "signature invalid (size)"); } if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (purpose_num, @@ -203,7 +133,8 @@ request_json_check_signature (struct MHD_Connection *connection, &signature, pub)) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "signature invalid (did not verify)\n"); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "signature invalid (did not verify)\n"); return TALER_MINT_reply_json_pack (connection, MHD_HTTP_UNAUTHORIZED, "{s:s}", @@ -216,7 +147,7 @@ request_json_check_signature (struct MHD_Connection *connection, /** - * Handle a "/refresh/melt" request after the first parsing has happened. + * 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. * @@ -228,114 +159,21 @@ request_json_check_signature (struct MHD_Connection *connection, * @return MHD result code */ static int -handle_refresh_melt_json (struct MHD_Connection *connection, - const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, - const json_t *new_denoms, - const json_t *melt_coins, - const json_t *melt_sig_json) +handle_refresh_melt_binary (struct MHD_Connection *connection, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + unsigned int num_new_denoms, + struct GNUNET_CRYPTO_rsa_PublicKey *const*denom_pubs, + unsigned int coin_count, + struct TALER_CoinPublicInfo *coin_public_infos, + const json_t *melt_sig_json) { int res; - unsigned int num_new_denoms; unsigned int i; - struct GNUNET_CRYPTO_rsa_PublicKey **denom_pubs; - struct TALER_CoinPublicInfo *coin_public_infos; - unsigned int coin_count; struct GNUNET_HashContext *hash_context; struct GNUNET_HashCode melt_hash; - struct MintKeyState *key_state; struct RefreshMeltSignatureBody body; char *buf; size_t buf_size; - struct TALER_MINT_DenomKeyIssuePriv *dki; - - num_new_denoms = json_array_size (new_denoms); - - denom_pubs = GNUNET_malloc (num_new_denoms * - sizeof (struct GNUNET_CRYPTO_rsa_PublicKey *)); - - for (i=0;i<num_new_denoms;i++) - { - res = GNUNET_MINT_parse_navigate_json (connection, new_denoms, - JNAV_INDEX, (int) i, - JNAV_RET_DATA_VAR, - &buf, - &buf_size); - if (GNUNET_OK != res) - { - GNUNET_free (denom_pubs); - /* FIXME: proper cleanup! */ - return res; - } - denom_pubs[i] = GNUNET_CRYPTO_rsa_public_key_decode (buf, buf_size); - GNUNET_free (buf); - if (NULL == denom_pubs[i]) - { - GNUNET_free (denom_pubs); - /* FIXME: proper cleanup! */ - /* FIXME: generate error reply */ - return GNUNET_SYSERR; - } - } - - - - - coin_count = json_array_size (melt_coins); - coin_public_infos = GNUNET_malloc (coin_count * - sizeof (struct TALER_CoinPublicInfo)); - key_state = TALER_MINT_key_state_acquire (); - for (i = 0; i < coin_count; i++) - { - /* decode JSON data on coin to melt */ - res = request_json_require_coin_public_info (connection, - json_array_get (melt_coins, i), - &coin_public_infos[i]); - if (GNUNET_OK != res) - { - GNUNET_break (GNUNET_SYSERR != res); - // FIXME: leaks! - TALER_MINT_key_state_release (key_state); - return res; - } - /* check that this coin's private key was used to sign that - we should melt it */ - if (GNUNET_OK != - (res = check_confirm_signature (connection, - json_array_get (melt_coins, i), - &coin_public_infos[i].coin_pub, - refresh_session_pub))) - { - GNUNET_break (GNUNET_SYSERR != res); - // FIXME: leaks! - TALER_MINT_key_state_release (key_state); - return res; - } - /* check coin denomination is valid */ - dki = TALER_MINT_get_denom_key (key_state, - coin_public_infos[i].denom_pub); - if (NULL == dki) - { - TALER_MINT_key_state_release (key_state); - LOG_WARNING ("Unknown denomination key in /refresh/melt request\n"); - TALER_MINT_key_state_release (key_state); - return TALER_MINT_reply_arg_invalid (connection, - "melt_coins"); - } - /* check mint signature on the coin */ - if (GNUNET_OK != - TALER_test_coin_valid (&coin_public_infos[i])) - { - // FIXME: leaks! - TALER_MINT_key_state_release (key_state); - return (MHD_YES == - TALER_MINT_reply_json_pack (connection, - MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", "coin invalid")) - ? GNUNET_NO : GNUNET_SYSERR; - } - } - TALER_MINT_key_state_release (key_state); /* check that signature from the session public key is ok */ hash_context = GNUNET_CRYPTO_hash_context_start (); @@ -364,19 +202,202 @@ handle_refresh_melt_json (struct MHD_Connection *connection, melt_sig_json, refresh_session_pub, &body.purpose))) - { - // FIXME: generate proper error reply return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; + + return TALER_MINT_db_execute_refresh_melt (connection, + refresh_session_pub, + num_new_denoms, + denom_pubs, + coin_count, + coin_public_infos); +} + + +/** + * 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. + * + * @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 + * @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 +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) +{ + 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 GNUNET_MINT_ParseFieldSpec spec[] = + { + TALER_MINT_PARSE_FIXED ("coin_pub", &r_public_info->coin_pub), + TALER_MINT_PARSE_RSA_SIGNATURE ("denom_sig", &sig), + TALER_MINT_PARSE_RSA_PUBLIC_KEY ("denom_pub", &pk), + TALER_MINT_PARSE_FIXED ("confirm_sig", &melt_sig), + TALER_MINT_PARSE_END + }; + + ret = TALER_MINT_parse_json_data (connection, + coin_info, + spec); + if (GNUNET_OK != ret) + return ret; + /* TODO: include amount of coin value to be melted here!? */ + body.purpose.size = htonl (sizeof (struct RefreshMeltConfirmSignRequestBody)); + body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_CONFIRM); + body.session_pub = *session_pub; + if (GNUNET_OK != + GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_REFRESH_MELT_CONFIRM, + &body.purpose, + &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, + "{s:s}", + "error", "signature invalid")) + return GNUNET_SYSERR; + return GNUNET_NO; + } + key_state = TALER_MINT_key_state_acquire (); + dki = TALER_MINT_get_denom_key (key_state, + pk); + /* FIXME: need to check if denomination key is still + valid for issuing! (#3634) */ + if (NULL == dki) + { + TALER_MINT_key_state_release (key_state); + LOG_WARNING ("Unknown denomination key in /refresh/melt request\n"); + return TALER_MINT_reply_arg_invalid (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; + } + return GNUNET_OK; +} - res = TALER_MINT_db_execute_refresh_melt (connection, - refresh_session_pub, - num_new_denoms, - denom_pubs, - coin_count, - coin_public_infos); - // FIXME: free memory + +/** + * Handle a "/refresh/melt" request after the first 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. This function + * parses the JSON arrays and then passes processing on to + * #handle_refresh_melt_binary(). + * + * @param connection the MHD connection to handle + * @param refresh_session_pub public key of the melt operation + * @param new_denoms array of denomination keys + * @param melt_coins array of coins to melt + * @param melt_sig_json signature affirming the melt operation + * @return MHD result code + */ +static int +handle_refresh_melt_json (struct MHD_Connection *connection, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + const json_t *new_denoms, + const json_t *melt_coins, + const json_t *melt_sig_json) +{ + int res; + unsigned int i; + unsigned int j; + struct GNUNET_CRYPTO_rsa_PublicKey **denom_pubs; + unsigned int num_new_denoms; + struct TALER_CoinPublicInfo *coin_public_infos; + unsigned int coin_count; + + num_new_denoms = json_array_size (new_denoms); + denom_pubs = GNUNET_malloc (num_new_denoms * + sizeof (struct GNUNET_CRYPTO_rsa_PublicKey *)); + for (i=0;i<num_new_denoms;i++) + { + res = GNUNET_MINT_parse_navigate_json (connection, new_denoms, + JNAV_INDEX, (int) i, + JNAV_RET_RSA_PUBLIC_KEY, &denom_pubs[i]); + if (GNUNET_OK != res) + { + for (j=0;j<i;j++) + GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j]); + GNUNET_free (denom_pubs); + return res; + } + } + + coin_count = json_array_size (melt_coins); + coin_public_infos = GNUNET_malloc (coin_count * + sizeof (struct TALER_CoinPublicInfo)); + 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]); + if (GNUNET_OK != res) + { + for (j=0;j<i;j++) + { + GNUNET_CRYPTO_rsa_public_key_free (coin_public_infos[j].denom_pub); + GNUNET_CRYPTO_rsa_signature_free (coin_public_infos[j].denom_sig); + } + GNUNET_free (coin_public_infos); + for (j=0;j<num_new_denoms;j++) + GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j]); + GNUNET_free (denom_pubs); + return (GNUNET_NO == res) ? MHD_YES : MHD_NO; + } + } + + res = handle_refresh_melt_binary (connection, + refresh_session_pub, + num_new_denoms, + denom_pubs, + coin_count, + coin_public_infos, + melt_sig_json); + for (j=0;j<coin_count;j++) + { + GNUNET_CRYPTO_rsa_public_key_free (coin_public_infos[j].denom_pub); + GNUNET_CRYPTO_rsa_signature_free (coin_public_infos[j].denom_sig); + } + GNUNET_free (coin_public_infos); + for (j=0;j<num_new_denoms;j++) + { + GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j]); + } + GNUNET_free (denom_pubs); return res; } @@ -660,10 +681,11 @@ TALER_MINT_handler_refresh_commit (struct RequestHandler *rh, body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_COMMIT); body.purpose.size = htonl (sizeof (struct RefreshCommitSignatureBody)); - if (GNUNET_OK != (res = request_json_check_signature (connection, - commit_sig_json, - &refresh_session_pub, - &body.purpose))) + if (GNUNET_OK != + (res = request_json_check_signature (connection, + commit_sig_json, + &refresh_session_pub, + &body.purpose))) { return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; } |