commit 9f66bf52f2a367ff6ab6be019229ba03dace5c9f
parent 827ab3c1f394e40b528a272ed2040e5a59551886
Author: Christian Grothoff <grothoff@gnunet.org>
Date: Tue, 14 Jul 2026 18:58:51 +0200
fix retransmission logic
Diffstat:
7 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/src/challenger/challenger-httpd.c b/src/challenger/challenger-httpd.c
@@ -566,6 +566,19 @@ run (void *cls,
CH_token_expiration = GNUNET_TIME_UNIT_HOURS;
}
if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (config,
+ "CHALLENGER",
+ "PIN_RETRANSMISSION_FREQUENCY",
+ &CH_pin_retransmission_frequency))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
+ "CHALLENGER",
+ "PIN_RETRANSMISSION_FREQUENCY");
+ CH_pin_retransmission_frequency
+ = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES,
+ 5);
+ }
+ if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (config,
"CHALLENGER",
"AUTH_COMMAND",
diff --git a/src/challenger/challenger-httpd_authorize.c b/src/challenger/challenger-httpd_authorize.c
@@ -325,7 +325,7 @@ CH_handler_authorize (struct CH_HandlerContext *hc,
GNUNET_TIME_absolute_to_timestamp (
GNUNET_TIME_absolute_add (
last_tx_time,
- CH_validation_duration))),
+ CH_pin_retransmission_frequency))),
GNUNET_JSON_pack_uint64 ("changes_left",
address_attempts_left)
);
diff --git a/src/challenger/challenger-httpd_challenge.c b/src/challenger/challenger-httpd_challenge.c
@@ -838,7 +838,7 @@ CH_handler_challenge (struct CH_HandlerContext *hc,
qs = CHALLENGERDB_challenge_set_address_and_pin (CH_context,
&bc->nonce,
bc->address,
- CH_validation_duration,
+ CH_pin_retransmission_frequency,
&bc->tan,
&bc->state,
&bc->last_tx_time,
@@ -966,7 +966,7 @@ CH_handler_challenge (struct CH_HandlerContext *hc,
GNUNET_TIME_absolute_to_timestamp (
GNUNET_TIME_absolute_add (
bc->last_tx_time,
- CH_validation_duration)))
+ CH_pin_retransmission_frequency)))
);
http_status = MHD_HTTP_OK;
resp = TALER_MHD_make_json_steal (args);
diff --git a/src/challenger/challenger.conf b/src/challenger/challenger.conf
@@ -30,6 +30,12 @@ VALIDATION_DURATION = 1d
# How long is an validation valid?
VALIDATION_EXPIRATION = 365d
+# Minimum time between two (re)transmissions of the PIN/TAN to the
+# same address. The user may request the challenge be re-sent, but
+# not more often than this. Must be (much) smaller than
+# VALIDATION_DURATION.
+PIN_RETRANSMISSION_FREQUENCY = 5 min
+
# Base URL of our service. Must end with '/'.
#BASE_URL = https://challenger.DOMAIN/
BASE_URL = http://localhost:9967/
diff --git a/src/challengerdb/challenge_set_address_and_pin.c b/src/challengerdb/challenge_set_address_and_pin.c
@@ -27,28 +27,44 @@
enum GNUNET_DB_QueryStatus
-CHALLENGERDB_challenge_set_address_and_pin (struct CHALLENGERDB_PostgresContext *ctx,
- const struct CHALLENGER_ValidationNonceP *nonce,
- const json_t *address,
- struct GNUNET_TIME_Relative validation_duration,
- uint32_t *tan,
- char **state,
- struct GNUNET_TIME_Absolute *last_tx_time,
- uint32_t *auth_attempts_left,
- bool *pin_transmit,
- char **client_redirect_uri,
- bool *address_refused,
- bool *solved)
+CHALLENGERDB_challenge_set_address_and_pin (struct
+ CHALLENGERDB_PostgresContext *ctx,
+ const struct
+ CHALLENGER_ValidationNonceP *nonce,
+ const json_t *address,
+ struct GNUNET_TIME_Relative
+ retransmission_frequency,
+ uint32_t *tan,
+ char **state,
+ struct GNUNET_TIME_Absolute *
+ last_tx_time,
+ uint32_t *auth_attempts_left,
+ bool *pin_transmit,
+ char **client_redirect_uri,
+ bool *address_refused,
+ bool *solved)
{
struct GNUNET_TIME_Absolute now
= GNUNET_TIME_absolute_get ();
- struct GNUNET_TIME_Absolute next_tx_time
+ /* We must gate retransmission on
+ last_tx_time + retransmission_frequency <= now
+ but 'last_tx_time' is only read (under FOR UPDATE) inside the stored
+ procedure, so we cannot form that sum here. We therefore pass the
+ equivalent, inverted form: the *newest* 'last_tx_time' for which a
+ retransmission is still due. The SQL then merely checks
+ last_tx_time <= retransmit_cutoff.
+ Note that this is deliberately a subtraction from 'now', not
+ 'now + retransmission_frequency': the value is compared against a
+ timestamp in the *past*. GNUNET_TIME_absolute_subtract() saturates at
+ zero rather than underflowing, so a FOREVER frequency correctly
+ degrades to "never retransmit". */
+ struct GNUNET_TIME_Absolute retransmit_cutoff
= GNUNET_TIME_absolute_subtract (now,
- validation_duration);
+ retransmission_frequency);
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (nonce),
TALER_PQ_query_param_json (address),
- GNUNET_PQ_query_param_absolute_time (&next_tx_time),
+ GNUNET_PQ_query_param_absolute_time (&retransmit_cutoff),
GNUNET_PQ_query_param_absolute_time (&now),
GNUNET_PQ_query_param_uint32 (tan),
GNUNET_PQ_query_param_end
diff --git a/src/challengerdb/challenger_do_challenge_set_address_and_pin.sql b/src/challengerdb/challenger_do_challenge_set_address_and_pin.sql
@@ -15,10 +15,14 @@
--
-CREATE OR REPLACE FUNCTION challenger_do_challenge_set_address_and_pin (
+DROP FUNCTION IF EXISTS challenger_do_challenge_set_address_and_pin;
+CREATE FUNCTION challenger_do_challenge_set_address_and_pin (
IN in_nonce BYTEA,
IN in_address TEXT,
- IN in_next_tx_time INT8,
+ -- Newest last_tx_time for which a (re)transmission is still due, that is
+ -- 'now - retransmission_frequency'. Computed by the caller because
+ -- last_tx_time is only known here; see challenge_set_address_and_pin.c.
+ IN in_retransmit_cutoff INT8,
IN in_now INT8,
IN in_tan INT4,
OUT out_not_found BOOLEAN,
@@ -103,8 +107,9 @@ THEN
END IF;
IF ( (my_status.pin_transmissions_left > 0) AND
- (my_status.last_tx_time <= in_next_tx_time) )
+ (my_status.last_tx_time <= in_retransmit_cutoff) )
THEN
+ -- enough time has passed since the last transmission, so
-- we are changing the PIN, update counters
my_status.pin_transmissions_left = my_status.pin_transmissions_left - 1;
my_status.last_pin = in_tan;
diff --git a/src/include/challenger-database/challenge_set_address_and_pin.h b/src/include/challenger-database/challenge_set_address_and_pin.h
@@ -36,10 +36,11 @@
* @param cls
* @param nonce unique nonce to use to identify the validation
* @param address the new address to validate
- * @param validation_duration minimum time between transmissions
+ * @param retransmission_frequency minimum time that must have passed since the
+ * last transmission before the PIN is (re)transmitted to @a address again
* @param[in,out] tan set to the PIN/TAN last send to @a address, input should be random PIN/TAN to use if address did not change
* @param[out] state set to client's OAuth2 state if available
- * @param[out] last_tx_time set to the last time when we (presumably) send a PIN to @a address, input should be current time to use if the existing value for tx_time is past @a next_tx_time
+ * @param[out] last_tx_time set to the last time when we (presumably) send a PIN to @a address
* @param[out] pin_transmit set to true if we should transmit the @a last_pin to the @a address
* @param[out] auth_attempts_left set to number of attempts the user has left on this pin
* @param[out] client_redirect_uri redirection URI of the client (for reporting failures)
@@ -57,7 +58,7 @@ CHALLENGERDB_challenge_set_address_and_pin (
struct CHALLENGERDB_PostgresContext *ctx,
const struct CHALLENGER_ValidationNonceP *nonce,
const json_t *address,
- struct GNUNET_TIME_Relative validation_duration,
+ struct GNUNET_TIME_Relative retransmission_frequency,
uint32_t *tan,
char **state,
struct GNUNET_TIME_Absolute *last_tx_time,