Merge pull request #6787 from yuhaoth/pr/workaround-gnutls_anti_replay_fail
TLS 1.3: EarlyData: Workaround anti replay fail from GnuTLS
diff --git a/ChangeLog.d/workaround_gnutls_anti_replay_fail.txt b/ChangeLog.d/workaround_gnutls_anti_replay_fail.txt
new file mode 100644
index 0000000..cebc2b7
--- /dev/null
+++ b/ChangeLog.d/workaround_gnutls_anti_replay_fail.txt
@@ -0,0 +1,7 @@
+Bugfix
+ * In TLS 1.3, when using a ticket for session resumption, tweak its age
+ calculation on the client side. It prevents a server with more accurate
+ ticket timestamps (typically timestamps in milliseconds) compared to the
+ Mbed TLS ticket timestamps (in seconds) to compute a ticket age smaller
+ than the age computed and transmitted by the client and thus potentially
+ reject the ticket. Fix #6623.
diff --git a/library/ssl_tls13_client.c b/library/ssl_tls13_client.c
index f07c8b6..025183d 100644
--- a/library/ssl_tls13_client.c
+++ b/library/ssl_tls13_client.c
@@ -946,6 +946,21 @@
uint32_t obfuscated_ticket_age =
(uint32_t)( now - session->ticket_received );
+ /*
+ * The ticket timestamp is in seconds but the ticket age is in
+ * milliseconds. If the ticket was received at the end of a second and
+ * re-used here just at the beginning of the next second, the computed
+ * age `now - session->ticket_received` is equal to 1s thus 1000 ms
+ * while the actual age could be just a few milliseconds or tens of
+ * milliseconds. If the server has more accurate ticket timestamps
+ * (typically timestamps in milliseconds), as part of the processing of
+ * the ClientHello, it may compute a ticket lifetime smaller than the
+ * one computed here and potentially reject the ticket. To avoid that,
+ * remove one second to the ticket age if possible.
+ */
+ if( obfuscated_ticket_age > 0 )
+ obfuscated_ticket_age -= 1;
+
obfuscated_ticket_age *= 1000;
obfuscated_ticket_age += session->ticket_age_add;
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index 02ee7cf..304d10c 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -424,7 +424,7 @@
" reconnect=%%d number of reconnections using session resumption\n" \
" default: 0 (disabled)\n" \
" reco_server_name=%%s default: NULL\n" \
- " reco_delay=%%d default: 0 seconds\n" \
+ " reco_delay=%%d default: 0 millionseconds\n" \
" reco_mode=%%d 0: copy session, 1: serialize session\n" \
" default: 1\n" \
" reconnect_hard=%%d default: 0 (disabled)\n" \
@@ -3184,7 +3184,7 @@
#if defined(MBEDTLS_TIMING_C)
if( opt.reco_delay > 0 )
- mbedtls_net_usleep( 1000000 * opt.reco_delay );
+ mbedtls_net_usleep( 1000 * opt.reco_delay );
#endif
mbedtls_printf( " . Reconnecting with saved session..." );
diff --git a/tests/opt-testcases/tls13-misc.sh b/tests/opt-testcases/tls13-misc.sh
index 710fb34..3aaf3f3 100755
--- a/tests/opt-testcases/tls13-misc.sh
+++ b/tests/opt-testcases/tls13-misc.sh
@@ -264,9 +264,6 @@
0 \
-s "key exchange mode: ephemeral$"
-# skip the basic check now cause it will randomly trigger the anti-replay protection in gnutls_server
-# Add it back once we fix the issue
-skip_next_test
requires_gnutls_tls1_3
requires_config_enabled MBEDTLS_DEBUG_C
requires_config_enabled MBEDTLS_SSL_CLI_C
@@ -277,7 +274,7 @@
MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED
run_test "TLS 1.3 m->G: EarlyData: basic check, good" \
"$G_NEXT_SRV -d 10 --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL:+ECDHE-PSK:+PSK --earlydata --disable-client-cert" \
- "$P_CLI debug_level=4 early_data=1 reco_mode=1 reconnect=1 reco_delay=2" \
+ "$P_CLI debug_level=4 early_data=1 reco_mode=1 reconnect=1 reco_delay=900" \
1 \
-c "Reconnecting with saved session" \
-c "NewSessionTicket: early_data(42) extension received." \
@@ -298,7 +295,7 @@
MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED
run_test "TLS 1.3 m->G: EarlyData: no early_data in NewSessionTicket, good" \
"$G_NEXT_SRV -d 10 --priority=NORMAL:-VERS-ALL:+VERS-TLS1.3:+CIPHER-ALL:+ECDHE-PSK:+PSK --disable-client-cert" \
- "$P_CLI debug_level=4 early_data=1 reco_mode=1 reconnect=1 reco_delay=2" \
+ "$P_CLI debug_level=4 early_data=1 reco_mode=1 reconnect=1" \
0 \
-c "Reconnecting with saved session" \
-C "NewSessionTicket: early_data(42) extension received." \
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index fdd3662..c206283 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -3643,7 +3643,7 @@
requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
run_test "Session resume using tickets: timeout" \
"$P_SRV debug_level=3 tickets=1 cache_max=0 ticket_timeout=1" \
- "$P_CLI debug_level=3 tickets=1 reconnect=1 reco_delay=2" \
+ "$P_CLI debug_level=3 tickets=1 reconnect=1 reco_delay=2000" \
0 \
-c "client hello, adding session ticket extension" \
-s "found session ticket extension" \
@@ -3953,7 +3953,7 @@
requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
run_test "Session resume using tickets, DTLS: timeout" \
"$P_SRV debug_level=3 dtls=1 tickets=1 cache_max=0 ticket_timeout=1" \
- "$P_CLI debug_level=3 dtls=1 tickets=1 reconnect=1 skip_close_notify=1 reco_delay=2" \
+ "$P_CLI debug_level=3 dtls=1 tickets=1 reconnect=1 skip_close_notify=1 reco_delay=2000" \
0 \
-c "client hello, adding session ticket extension" \
-s "found session ticket extension" \
@@ -4077,7 +4077,7 @@
requires_config_enabled MBEDTLS_SSL_CACHE_C
run_test "Session resume using cache: timeout < delay" \
"$P_SRV debug_level=3 tickets=0 cache_timeout=1" \
- "$P_CLI debug_level=3 tickets=0 reconnect=1 reco_delay=2" \
+ "$P_CLI debug_level=3 tickets=0 reconnect=1 reco_delay=2000" \
0 \
-S "session successfully restored from cache" \
-S "session successfully restored from ticket" \
@@ -4088,7 +4088,7 @@
requires_config_enabled MBEDTLS_SSL_CACHE_C
run_test "Session resume using cache: no timeout" \
"$P_SRV debug_level=3 tickets=0 cache_timeout=0" \
- "$P_CLI debug_level=3 tickets=0 reconnect=1 reco_delay=2" \
+ "$P_CLI debug_level=3 tickets=0 reconnect=1 reco_delay=2000" \
0 \
-s "session successfully restored from cache" \
-S "session successfully restored from ticket" \
@@ -4224,7 +4224,7 @@
requires_config_enabled MBEDTLS_SSL_CACHE_C
run_test "Session resume using cache, DTLS: timeout < delay" \
"$P_SRV dtls=1 debug_level=3 tickets=0 cache_timeout=1" \
- "$P_CLI dtls=1 debug_level=3 tickets=0 reconnect=1 skip_close_notify=1 reco_delay=2" \
+ "$P_CLI dtls=1 debug_level=3 tickets=0 reconnect=1 skip_close_notify=1 reco_delay=2000" \
0 \
-S "session successfully restored from cache" \
-S "session successfully restored from ticket" \
@@ -4235,7 +4235,7 @@
requires_config_enabled MBEDTLS_SSL_CACHE_C
run_test "Session resume using cache, DTLS: no timeout" \
"$P_SRV dtls=1 debug_level=3 tickets=0 cache_timeout=0" \
- "$P_CLI dtls=1 debug_level=3 tickets=0 reconnect=1 skip_close_notify=1 reco_delay=2" \
+ "$P_CLI dtls=1 debug_level=3 tickets=0 reconnect=1 skip_close_notify=1 reco_delay=2000" \
0 \
-s "session successfully restored from cache" \
-S "session successfully restored from ticket" \
@@ -9891,7 +9891,7 @@
key_file=data_files/server8.key \
hs_timeout=10000-60000 \
force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 \
- mtu=1450 reconnect=1 skip_close_notify=1 reco_delay=1" \
+ mtu=1450 reconnect=1 skip_close_notify=1 reco_delay=1000" \
0 \
-S "autoreduction" \
-s "found fragmented DTLS handshake message" \