From 6d8ccc9fe7d328a2ac53263f289f2e309704e412 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 31 Jan 2015 15:42:00 +0100 Subject: [PATCH] starting to clean up /refresh/reveal DB logic --- src/mint/taler-mint-httpd_db.c | 411 +++++++++++++++----------- src/mint/taler-mint-httpd_responses.c | 20 ++ src/mint/taler-mint-httpd_responses.h | 12 + 3 files changed, 272 insertions(+), 171 deletions(-) diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index 39a0bffdd..5f14a89bf 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -801,6 +801,203 @@ helper_refresh_reveal_send_response (struct MHD_Connection *connection, } +/** + * Check if the given @a transfer_privs correspond to an honest + * commitment for the given session. + * Checks that the transfer private keys match their commitments. + * Then derives the shared secret for each kappa, and check that they match. + * + * @param connection the MHD connection to handle + * @param db_conn database connection to use + * @param refresh_session session to query + * @param off commitment offset to check + * @param num_oldcoins size of the @a transfer_privs and @a melts arrays + * @param num_newcoins number of newcoins being generated + * @param transfer_privs private transfer keys + * @param melts array of melted coins + * @return #GNUNET_OK if the committment was honest, + * #GNUNET_NO if there was a problem and we generated an error message + * #GNUNET_SYSERR if we could not even generate an error message + */ +static int +check_commitment (struct MHD_Connection *connection, + PGconn *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session, + unsigned int off, + unsigned int num_oldcoins, + unsigned int num_newcoins, + const struct GNUNET_CRYPTO_EcdsaPrivateKey *transfer_privs, + const struct RefreshMelt *melts) +{ + unsigned int j; + int res; + struct TALER_LinkSecret last_shared_secret; + int secret_initialized = GNUNET_NO; + + for (j = 0; j < num_oldcoins; j++) + { + struct RefreshCommitLink commit_link; + struct TALER_TransferSecret transfer_secret; + struct TALER_LinkSecret shared_secret; + struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub_check; + + res = TALER_MINT_DB_get_refresh_commit_link (db_conn, + refresh_session, + off, + j, + &commit_link); + if (GNUNET_OK != res) + { + GNUNET_break (0); + return (MHD_YES == TALER_MINT_reply_internal_db_error (connection)) + ? GNUNET_NO : GNUNET_SYSERR; + } + + GNUNET_CRYPTO_ecdsa_key_get_public (&transfer_privs[j], + &transfer_pub_check); + if (0 != + memcmp (&transfer_pub_check, + &commit_link.transfer_pub, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "transfer keys do not match\n"); + return (MHD_YES == TALER_MINT_reply_external_error (connection, + "Transfer private key missmatch")) + ? GNUNET_NO : GNUNET_SYSERR; + } + + /* We're converting key types here, which is not very nice + * but necessary and harmless (keys will be thrown away later). */ + /* FIXME: ECDHE/ECDSA-key type confusion! Can we reduce/avoid this? */ + if (GNUNET_OK != + GNUNET_CRYPTO_ecc_ecdh ((const struct GNUNET_CRYPTO_EcdhePrivateKey *) &transfer_privs[j], + (const struct GNUNET_CRYPTO_EcdhePublicKey *) &melts[j].coin.coin_pub, + &transfer_secret.key)) + { + GNUNET_break (0); + return (MHD_YES == TALER_MINT_reply_internal_error (connection, + "ECDH error")) + ? GNUNET_NO : GNUNET_SYSERR; + } + + if (GNUNET_OK != + TALER_transfer_decrypt (&commit_link.shared_secret_enc, + &transfer_secret, + &shared_secret)) + { + GNUNET_break (0); + return (MHD_YES == TALER_MINT_reply_internal_error (connection, + "Decryption error")) + ? GNUNET_NO : GNUNET_SYSERR; + } + + if (GNUNET_NO == secret_initialized) + { + secret_initialized = GNUNET_YES; + last_shared_secret = shared_secret; + } + else if (0 != memcmp (&shared_secret, + &last_shared_secret, + sizeof (struct GNUNET_HashCode))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "shared secrets do not match\n"); + return (MHD_YES == TALER_MINT_reply_external_error (connection, + "Shared secret missmatch")) + ? GNUNET_NO : GNUNET_SYSERR; + } + } + GNUNET_break (GNUNET_YES == secret_initialized); + + + /* Check that the commitments for all new coins were correct */ + for (j = 0; j < num_newcoins; j++) + { + struct RefreshCommitCoin commit_coin; + struct TALER_RefreshLinkDecrypted *link_data; + // struct BlindedSignaturePurpose *coin_ev_check; + struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub; + struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub; + struct GNUNET_HashCode h_msg; + char *buf; + size_t buf_len; + + res = TALER_MINT_DB_get_refresh_commit_coin (db_conn, + refresh_session, + off, + j, + &commit_coin); + if (GNUNET_OK != res) + { + GNUNET_break (0); + return (MHD_YES == TALER_MINT_reply_internal_db_error (connection)) + ? GNUNET_NO : GNUNET_SYSERR; + } + + link_data = TALER_refresh_decrypt (commit_coin.refresh_link, + &last_shared_secret); + if (NULL == link_data) + { + GNUNET_break (0); + return (MHD_YES == TALER_MINT_reply_internal_error (connection, + "Decryption error")) + ? GNUNET_NO : GNUNET_SYSERR; + } + + GNUNET_CRYPTO_ecdsa_key_get_public (&link_data->coin_priv, + &coin_pub); + /* FIXME: like the melts, + do get these ONCE outside of the kappa-loop! */ + denom_pub = TALER_MINT_DB_get_refresh_order (db_conn, + refresh_session, + j); + if (NULL == denom_pub) + { + GNUNET_break (0); + return (MHD_YES == TALER_MINT_reply_internal_db_error (connection)) + ? GNUNET_NO : GNUNET_SYSERR; + } + /* FIXME: we had envisioned a more complex scheme to derive + the message to sign for a blinded coin... + FIXME: we should have a function in util/ to do this! */ + GNUNET_CRYPTO_hash (&coin_pub, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), + &h_msg); + if (0 == (buf_len = + GNUNET_CRYPTO_rsa_blind (&h_msg, + link_data->blinding_key, + denom_pub, + &buf))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "blind failed\n"); + return (MHD_YES == TALER_MINT_reply_internal_error (connection, + "Blinding error")) + ? GNUNET_NO : GNUNET_SYSERR; + } + + if ( (buf_len != commit_coin.coin_ev_size) || + (0 != memcmp (buf, + commit_coin.coin_ev, + buf_len)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "blind envelope does not match for kappa=%u, old=%d\n", + off, + (int) j); + /* FIXME: return more specific error with exact offset */ + return (MHD_YES == TALER_MINT_reply_external_error (connection, + "Envelope missmatch")) + ? GNUNET_NO : GNUNET_SYSERR; + } + GNUNET_free (buf); + } + + return GNUNET_OK; +} + + /** * Execute a "/refresh/reveal". The client is revealing to us the * transfer keys for @a kappa-1 sets of coins. Verify that the @@ -826,6 +1023,7 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection, PGconn *db_conn; struct RefreshSession refresh_session; struct MintKeyState *key_state; + struct RefreshMelt *melts; unsigned int i; unsigned int j; unsigned int off; @@ -844,184 +1042,55 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection, "session_pub"); if (GNUNET_SYSERR == res) return TALER_MINT_reply_internal_db_error (connection); + if (0 == refresh_session.num_oldcoins) + { + GNUNET_break (0); + return TALER_MINT_reply_internal_db_error (connection); + } - /* Check that the transfer private keys match their commitments. - * Then derive the shared secret for each kappa, and check that they match. */ + if ( (refresh_session.noreveal_index >= refresh_session.kappa) || + (GNUNET_NO == refresh_session.has_commit_sig) ) + { + GNUNET_break (UINT16_MAX == refresh_session.noreveal_index); + GNUNET_break (GNUNET_NO == refresh_session.has_commit_sig); + return TALER_MINT_reply_external_error (connection, + "/refresh/commit must be executed first"); + } + + melts = GNUNET_malloc (refresh_session.num_oldcoins * + sizeof (struct RefreshMelt)); + for (j = 0; j < refresh_session.num_oldcoins; j++) + { + if (GNUNET_OK != + TALER_MINT_DB_get_refresh_melt (db_conn, + refresh_session_pub, + j, + &melts[j])) + { + GNUNET_break (0); + GNUNET_free (melts); + return TALER_MINT_reply_internal_db_error (connection); + } + } off = 0; for (i = 0; i < refresh_session.kappa - 1; i++) { - struct TALER_LinkSecret last_shared_secret; - int secret_initialized = GNUNET_NO; - if (i == refresh_session.noreveal_index) off = 1; - - for (j = 0; j < refresh_session.num_oldcoins; j++) - { - struct RefreshCommitLink commit_link; - struct TALER_TransferSecret transfer_secret; - struct TALER_LinkSecret shared_secret; - struct RefreshMelt melt; - - res = TALER_MINT_DB_get_refresh_commit_link (db_conn, - refresh_session_pub, - i + off, j, - &commit_link); - if (GNUNET_OK != res) - { - GNUNET_break (0); - // FIXME: return 'internal error'? - return MHD_NO; - } - - res = TALER_MINT_DB_get_refresh_melt (db_conn, - refresh_session_pub, - j, - &melt); - if (GNUNET_OK != res) - { - GNUNET_break (0); - // FIXME: return 'internal error'? - return MHD_NO; - } - - /* We're converting key types here, which is not very nice - * but necessary and harmless (keys will be thrown away later). */ - /* FIXME: ECDHE/ECDSA-key type confusion! Can we reduce/avoid this? */ - if (GNUNET_OK != - GNUNET_CRYPTO_ecc_ecdh ((const struct GNUNET_CRYPTO_EcdhePrivateKey *) &transfer_privs[i+off][j], - (const struct GNUNET_CRYPTO_EcdhePublicKey *) &melt.coin.coin_pub, - &transfer_secret.key)) - { - GNUNET_break (0); - // FIXME: return 'internal error'? - return MHD_NO; - } - - if (GNUNET_OK != - TALER_transfer_decrypt (&commit_link.shared_secret_enc, - &transfer_secret, - &shared_secret)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "decryption failed\n"); - // FIXME: return 'internal error'? - return MHD_NO; - } - - if (GNUNET_NO == secret_initialized) - { - secret_initialized = GNUNET_YES; - last_shared_secret = shared_secret; - } - else if (0 != memcmp (&shared_secret, - &last_shared_secret, - sizeof (struct GNUNET_HashCode))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "shared secrets do not match\n"); - // FIXME: return error code! - return MHD_NO; - } - - { - struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub_check; - - GNUNET_CRYPTO_ecdsa_key_get_public (&transfer_privs[i+off][j], - &transfer_pub_check); - if (0 != - memcmp (&transfer_pub_check, - &commit_link.transfer_pub, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "transfer keys do not match\n"); - // FIXME: return error code! - return MHD_NO; - } - } - } - - /* Check that the commitments for all new coins were correct */ - for (j = 0; j < refresh_session.num_newcoins; j++) - { - struct RefreshCommitCoin commit_coin; - struct TALER_RefreshLinkDecrypted *link_data; - // struct BlindedSignaturePurpose *coin_ev_check; - struct GNUNET_CRYPTO_EcdsaPublicKey coin_pub; - struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub; - struct GNUNET_HashCode h_msg; - char *buf; - size_t buf_len; - - res = TALER_MINT_DB_get_refresh_commit_coin (db_conn, - refresh_session_pub, - i+off, j, - &commit_coin); - if (GNUNET_OK != res) - { - GNUNET_break (0); - // FIXME: return error code! - return MHD_NO; - } - - link_data = TALER_refresh_decrypt (commit_coin.refresh_link, - &last_shared_secret); - if (NULL == link_data) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "decryption failed\n"); - // FIXME: return error code! - return MHD_NO; - } - - GNUNET_CRYPTO_ecdsa_key_get_public (&link_data->coin_priv, - &coin_pub); - denom_pub = TALER_MINT_DB_get_refresh_order (db_conn, - refresh_session_pub, - j); - if (NULL == denom_pub) - { - GNUNET_break (0); - // FIXME: return error code! - return MHD_NO; - } - /* FIXME: we had envisioned a more complex scheme to derive - the message to sign for a blinded coin... */ - GNUNET_CRYPTO_hash (&coin_pub, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), - &h_msg); - if (0 == (buf_len = - GNUNET_CRYPTO_rsa_blind (&h_msg, - link_data->blinding_key, - denom_pub, - &buf))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "blind failed\n"); - // FIXME: return error code! - return MHD_NO; - } - - if ( (buf_len != commit_coin.coin_ev_size) || - (0 != memcmp (buf, - commit_coin.coin_ev, - buf_len)) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "blind envelope does not match for kappa=%d, old=%d\n", - (int) (i+off), (int) j); - // FIXME: return error code! - GNUNET_free (buf); - return MHD_NO; - } - GNUNET_free (buf); - - } + if (GNUNET_OK != + (res = check_commitment (connection, + db_conn, + refresh_session_pub, + i + off, + refresh_session.num_oldcoins, + refresh_session.num_newcoins, + transfer_privs[i + off], + melts))) + return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } - + /* Client request OK, start transaction */ if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn)) { @@ -1044,7 +1113,7 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection, if (GNUNET_OK != res) { GNUNET_break (0); - // FIXME: return error code! + // FIXME: return error code! return MHD_NO; } denom_pub = TALER_MINT_DB_get_refresh_order (db_conn, @@ -1053,7 +1122,7 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection, if (NULL == denom_pub) { GNUNET_break (0); - // FIXME: return error code! + // FIXME: return error code! return MHD_NO; } diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index bc2c5e488..264e5a126 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -170,6 +170,26 @@ TALER_MINT_reply_internal_error (struct MHD_Connection *connection, } +/** + * Send a response indicating an external error. + * + * @param connection the MHD connection to use + * @param hint hint about the error's nature + * @return a MHD result code + */ +int +TALER_MINT_reply_external_error (struct MHD_Connection *connection, + const char *hint) +{ + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:s}", + "error", "client error", + "hint", hint); +} + + + /** * Send a response indicating an error committing a * transaction (concurrent interference). diff --git a/src/mint/taler-mint-httpd_responses.h b/src/mint/taler-mint-httpd_responses.h index 3e8f3f3ff..c6f31f1dc 100644 --- a/src/mint/taler-mint-httpd_responses.h +++ b/src/mint/taler-mint-httpd_responses.h @@ -112,6 +112,18 @@ TALER_MINT_reply_internal_error (struct MHD_Connection *connection, const char *hint); +/** + * Send a response indicating an external error. + * + * @param connection the MHD connection to use + * @param hint hint about the error's nature + * @return a MHD result code + */ +int +TALER_MINT_reply_external_error (struct MHD_Connection *connection, + const char *hint); + + /** * Send a response indicating an error committing a * transaction (concurrent interference).