diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 44c3d14cf..f0f6784f4 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -443,35 +443,39 @@ TEH_handler_deposit (struct MHD_Connection *connection, return mret; } now = GNUNET_TIME_absolute_get (); + (void) GNUNET_TIME_round_abs (&now); if (now.abs_value_us >= dk->meta.expire_deposit.abs_value_us) { /* This denomination is past the expiration time for deposits */ GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( + return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - MHD_HTTP_GONE, + &deposit.coin.denom_pub_hash, + now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - NULL); + "DEPOSIT"); } if (now.abs_value_us < dk->meta.start.abs_value_us) { /* This denomination is not yet valid */ GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( + return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - MHD_HTTP_PRECONDITION_FAILED, + &deposit.coin.denom_pub_hash, + now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - NULL); + "DEPOSIT"); } if (dk->recoup_possible) { /* This denomination has been revoked */ GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( + return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - MHD_HTTP_GONE, + &deposit.coin.denom_pub_hash, + now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, - NULL); + "DEPOSIT"); } deposit.deposit_fee = dk->meta.fee_deposit; diff --git a/src/exchange/taler-exchange-httpd_melt.c b/src/exchange/taler-exchange-httpd_melt.c index 5e9ae7f0d..7276f9a65 100644 --- a/src/exchange/taler-exchange-httpd_melt.c +++ b/src/exchange/taler-exchange-httpd_melt.c @@ -476,23 +476,26 @@ check_for_denomination_key (struct MHD_Connection *connection, if (NULL == dk) return mret; now = GNUNET_TIME_absolute_get (); + (void) GNUNET_TIME_round_abs (&now); if (now.abs_value_us >= dk->meta.expire_legal.abs_value_us) { /* Way too late now, even zombies have expired */ - return TALER_MHD_reply_with_error ( + return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - MHD_HTTP_GONE, + &rmc->refresh_session.coin.denom_pub_hash, + now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - NULL); + "MELT"); } if (now.abs_value_us < dk->meta.start.abs_value_us) { /* This denomination is not yet valid */ - return TALER_MHD_reply_with_error ( + return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - MHD_HTTP_PRECONDITION_FAILED, + &rmc->refresh_session.coin.denom_pub_hash, + now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - NULL); + "MELT"); } if (now.abs_value_us >= dk->meta.expire_deposit.abs_value_us) { @@ -524,11 +527,12 @@ check_for_denomination_key (struct MHD_Connection *connection, if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) { /* We never saw this coin before, so _this_ justification is not OK */ - return TALER_MHD_reply_with_error ( + return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - MHD_HTTP_GONE, + &rmc->refresh_session.coin.denom_pub_hash, + now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - NULL); + "MELT"); } else { diff --git a/src/exchange/taler-exchange-httpd_recoup.c b/src/exchange/taler-exchange-httpd_recoup.c index c1aadb83e..cd12be0d9 100644 --- a/src/exchange/taler-exchange-httpd_recoup.c +++ b/src/exchange/taler-exchange-httpd_recoup.c @@ -373,32 +373,36 @@ verify_and_execute_recoup (struct MHD_Connection *connection, if (NULL == dk) return mret; now = GNUNET_TIME_absolute_get (); + (void) GNUNET_TIME_round_abs (&now); if (now.abs_value_us >= dk->meta.expire_deposit.abs_value_us) { /* This denomination is past the expiration time for recoup */ - return TALER_MHD_reply_with_error ( + return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - MHD_HTTP_GONE, + &coin->denom_pub_hash, + now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - NULL); + "RECOUP"); } if (now.abs_value_us < dk->meta.start.abs_value_us) { /* This denomination is not yet valid */ - return TALER_MHD_reply_with_error ( + return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - MHD_HTTP_PRECONDITION_FAILED, + &coin->denom_pub_hash, + now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - NULL); + "RECOUP"); } if (! dk->recoup_possible) { /* This denomination is not eligible for recoup */ - return TALER_MHD_reply_with_error ( + return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - MHD_HTTP_NOT_FOUND, + &coin->denom_pub_hash, + now, TALER_EC_EXCHANGE_RECOUP_NOT_ELIGIBLE, - NULL); + "RECOUP"); } pc.value = dk->meta.value; diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c index b6b1849c7..d915aefce 100644 --- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c +++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c @@ -558,6 +558,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection, } /* Parse denomination key hashes */ now = GNUNET_TIME_absolute_get (); + (void) GNUNET_TIME_round_abs (&now); for (unsigned int i = 0; i= dks[i]->meta.expire_withdraw.abs_value_us) { /* This denomination is past the expiration time for withdraws */ - return TALER_MHD_reply_with_error ( + return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - MHD_HTTP_GONE, + &dk_h[i], + now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - NULL); + "REVEAL"); } if (now.abs_value_us < dks[i]->meta.start.abs_value_us) { /* This denomination is not yet valid */ - return TALER_MHD_reply_with_error ( + return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - MHD_HTTP_PRECONDITION_FAILED, + &dk_h[i], + now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - NULL); + "REVEAL"); } if (dks[i]->recoup_possible) { diff --git a/src/exchange/taler-exchange-httpd_refund.c b/src/exchange/taler-exchange-httpd_refund.c index 56d987d94..d0865dd8e 100644 --- a/src/exchange/taler-exchange-httpd_refund.c +++ b/src/exchange/taler-exchange-httpd_refund.c @@ -461,17 +461,6 @@ verify_and_execute_refund (struct MHD_Connection *connection, GNUNET_break (0); return mret; } - - if (GNUNET_TIME_absolute_get ().abs_value_us >= - dk->meta.expire_deposit.abs_value_us) - { - /* This denomination is past the expiration time for deposits, and thus refunds */ - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_GONE, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - NULL); - } refund->details.refund_fee = dk->meta.fee_refund; } diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index 234074747..4bb3ffd48 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -466,6 +466,58 @@ TEH_RESPONSE_reply_unknown_denom_pub_hash ( } +MHD_RESULT +TEH_RESPONSE_reply_expired_denom_pub_hash ( + struct MHD_Connection *connection, + const struct GNUNET_HashCode *dph, + struct GNUNET_TIME_Absolute now, + enum TALER_ErrorCode ec, + const char *oper) +{ + struct TALER_ExchangePublicKeyP epub; + struct TALER_ExchangeSignatureP esig; + enum TALER_ErrorCode ecr; + struct TALER_DenominationExpiredAffirmationPS dua = { + .purpose.size = htonl (sizeof (dua)), + .purpose.purpose = htonl ( + TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_EXPIRED), + .timestamp = GNUNET_TIME_absolute_hton (now), + .h_denom_pub = *dph, + }; + + strncpy (dua.operation, + oper, + sizeof (dua.operation)); + ecr = TEH_keys_exchange_sign (&dua, + &epub, + &esig); + if (TALER_EC_NONE != ecr) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + ec, + NULL); + } + return TALER_MHD_reply_json_pack ( + connection, + MHD_HTTP_GONE, + "{s:I,s:s,s:o,s:o,s:o,s:o}", + "code", + ec, + "oper", + oper, + "timestamp", + GNUNET_JSON_from_time_abs (now), + "exchange_pub", + GNUNET_JSON_from_data_auto (&epub), + "exchange_sig", + GNUNET_JSON_from_data_auto (&esig), + "h_denom_pub", + GNUNET_JSON_from_data_auto (dph)); +} + + /** * Send proof that a request is invalid to client because of * insufficient funds. This function will create a message with all diff --git a/src/exchange/taler-exchange-httpd_responses.h b/src/exchange/taler-exchange-httpd_responses.h index 7182629eb..d4acd2132 100644 --- a/src/exchange/taler-exchange-httpd_responses.h +++ b/src/exchange/taler-exchange-httpd_responses.h @@ -62,6 +62,26 @@ TEH_RESPONSE_reply_unknown_denom_pub_hash ( const struct GNUNET_HashCode *dph); +/** + * Send assertion that the given denomination key hash + * is not usable (typically expired) at this time. + * + * @param connection connection to the client + * @param dph denomination public key hash + * @param now timestamp to use + * @param ec error code to use + * @param name of the operation that is not allowed at this time + * @return MHD result code + */ +MHD_RESULT +TEH_RESPONSE_reply_expired_denom_pub_hash ( + struct MHD_Connection *connection, + const struct GNUNET_HashCode *dph, + struct GNUNET_TIME_Absolute now, + enum TALER_ErrorCode ec, + const char *oper); + + /** * Send proof that a request is invalid to client because of * insufficient funds. This function will create a message with all diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c index 5833fcf48..d0216c4c4 100644 --- a/src/exchange/taler-exchange-httpd_withdraw.c +++ b/src/exchange/taler-exchange-httpd_withdraw.c @@ -391,35 +391,39 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh, return mret; } now = GNUNET_TIME_absolute_get (); + (void) GNUNET_TIME_round_abs (&now); if (now.abs_value_us >= dk->meta.expire_withdraw.abs_value_us) { /* This denomination is past the expiration time for withdraws */ GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( + return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - MHD_HTTP_GONE, + &wc.denom_pub_hash, + now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - NULL); + "WITHDRAW"); } if (now.abs_value_us < dk->meta.start.abs_value_us) { /* This denomination is not yet valid */ GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( + return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - MHD_HTTP_PRECONDITION_FAILED, + &wc.denom_pub_hash, + now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - NULL); + "WITHDRAW"); } if (dk->recoup_possible) { /* This denomination has been revoked */ GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( + return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, - MHD_HTTP_GONE, + &wc.denom_pub_hash, + now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, - NULL); + "WITHDRAW"); } } diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index 1a64d52f6..6ed71153d 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -161,6 +161,13 @@ #define TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_UNKNOWN 1042 +/** + * Signature where the Exchange confirms that it does not consider a denomination valid for the given operation + * at this time. + */ +#define TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_EXPIRED 1043 + + /**********************/ /* Auditor signatures */ /**********************/ @@ -1543,6 +1550,37 @@ struct TALER_DenominationUnknownAffirmationPS }; +/** + * Response by which the exchange affirms that it does not + * currently consider the given denomination to be valid + * for the requested operation. + */ +struct TALER_DenominationExpiredAffirmationPS +{ + + /** + * Purpose is #TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_EXPIRED + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * When did the exchange sign this message. + */ + struct GNUNET_TIME_AbsoluteNBO timestamp; + + /** + * Name of the operation that is not allowed at this time. Might NOT be 0-terminated, but is padded with 0s. + */ + char operation[8]; + + /** + * Hash of the public denomination key we do not know. + */ + struct GNUNET_HashCode h_denom_pub; + +}; + + /** * Response by which the exchange affirms that it has * closed a reserve and send back the funds.