From 40a60f4a21338b47e7bed5fa4f8cecd586b74784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Tue, 3 Nov 2026 09:50:42 +0100 Subject: [PATCH 1/3] Fix FreeRTOS millisecond clock overflow TimeNowInMilliseconds multiplied xTaskGetTickCount() by 1000 in TickType_t before widening it to 64 bits. With a 32 bit tick count the product wrapped after 2^32 / 1000 ticks, about 71 minutes at 1 kHz, and the clock jumped back to 0. Widen the tick count before multiplying. The FREESCALE_FREE_RTOS and FREESCALE_KSDK_FREERTOS variants divided the tick count by configTICK_RATE_HZ / 1000, which is 0 below 1 kHz, and the 32 bit variant divided in float, which drops milliseconds once the tick count passes 2^24. Use the same integer conversion there. TLS 1.3 ticket ages and the DTLS 1.3 timers read this clock. --- src/tls13.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tls13.c b/src/tls13.c index 1a4e34e73c6..bf6b3ce8061 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -2284,8 +2284,8 @@ int DeriveTls13Keys(WOLFSSL* ssl, int secret, int side, int store) */ word32 TimeNowInMilliseconds(void) { - return (unsigned int)(((float)xTaskGetTickCount()) / - (configTICK_RATE_HZ / 1000)); + return (word32)((uint64_t)xTaskGetTickCount() * 1000 / + configTICK_RATE_HZ); } #elif defined(FREESCALE_KSDK_BM) #include "lwip/sys.h" /* lwIP */ @@ -2371,7 +2371,7 @@ int DeriveTls13Keys(WOLFSSL* ssl, int secret, int side, int store) #elif defined(FREERTOS) word32 TimeNowInMilliseconds(void) { - return (word32)((uint64_t)(xTaskGetTickCount() * 1000) / + return (word32)((uint64_t)xTaskGetTickCount() * 1000 / configTICK_RATE_HZ); } #else @@ -2578,7 +2578,8 @@ int DeriveTls13Keys(WOLFSSL* ssl, int secret, int side, int store) */ sword64 TimeNowInMilliseconds(void) { - return (sword64)xTaskGetTickCount() / (configTICK_RATE_HZ / 1000); + return (sword64)((uint64_t)xTaskGetTickCount() * 1000 / + configTICK_RATE_HZ); } #elif defined(FREESCALE_KSDK_BM) #include "lwip/sys.h" /* lwIP */ @@ -2680,7 +2681,7 @@ int DeriveTls13Keys(WOLFSSL* ssl, int secret, int side, int store) #elif defined(FREERTOS) sword64 TimeNowInMilliseconds(void) { - return (sword64)((uint64_t)(xTaskGetTickCount() * 1000) / + return (sword64)((uint64_t)xTaskGetTickCount() * 1000 / configTICK_RATE_HZ); } #else From ebc2d9625fe18ca33c25698553a9c0a175cd2c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Tue, 3 Nov 2026 11:29:15 +0100 Subject: [PATCH 2/3] Merge the TimeNowInMilliseconds ladders into internal.c tls13.c kept two copies of the TimeNowInMilliseconds platform ladder, one returning word32 for WOLFSSL_32BIT_MILLI_TIME and one returning sword64, with the same doc comment repeated in most branches. Keep one ladder and pick the return type with a local macro. Move it next to LowResTimer in internal.c, so both platform clocks live in one place and the millisecond clock no longer depends on tls13.c. The sys/time.h include moves with it, and the TIME_OVERRIDES branch reuses the time_t and XTIME declarations of the LowResTimer branch above it. Each branch computes what both copies did, with three exceptions. The gettimeofday() and FUSION_RTOS branches convert tv_sec to the return type before multiplying, so the 32 bit variant no longer overflows a 32 bit long and gives the same result mod 2^32. The 32 bit MICROCHIP_TCPIP variants cast the tick count before dividing, as the 64 bit ones did, which gives the same value for their 32 bit tick counters. The LinuxKM branch keeps both bodies, since the 32 bit one uses the NMI safe wc_linuxkm_time_epoch_msecs(). --- src/internal.c | 234 +++++++++++++++++++ src/tls13.c | 614 ------------------------------------------------- 2 files changed, 234 insertions(+), 614 deletions(-) diff --git a/src/internal.c b/src/internal.c index 81f89bd4c90..4d04525fece 100644 --- a/src/internal.c +++ b/src/internal.c @@ -205,6 +205,12 @@ #include #endif +#if defined(__MACH__) || defined(__FreeBSD__) || \ + defined(__INCLUDE_NUTTX_CONFIG_H) || defined(WOLFSSL_RIOT_OS) +#include +#endif /* __MACH__ || __FreeBSD__ || + __INCLUDE_NUTTX_CONFIG_H || WOLFSSL_RIOT_OS */ + #define ERROR_OUT(err, eLabel) { ret = (int)(err); goto eLabel; } #define CHECK_RET(ret, exp, eLabel) do { if (((ret) = (exp)) != 0) goto eLabel;\ @@ -12475,6 +12481,234 @@ ProtocolVersion MakeDTLSv1_3(void) */ #endif /* !NO_ASN_TIME */ +#if !defined(NO_TLS) && defined(WOLFSSL_TLS13) && \ + (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) || \ + defined(WOLFSSL_DTLS13)) +#ifndef NO_ASN_TIME +#ifdef WOLFSSL_32BIT_MILLI_TIME + #define MS_TIME word32 +#else + #define MS_TIME sword64 +#endif + +/* The time in milliseconds, for ticket ages and the DTLS timers. */ +#if defined(USER_TICKS) + /* The application supplies TimeNowInMilliseconds(). */ +#elif defined(WOLFSSL_OPTEE) + MS_TIME TimeNowInMilliseconds(void) + { + TEE_Time now; + + /* Monotonic within the TA, and not the normal world's clock. */ + TEE_GetSystemTime(&now); + + return ((MS_TIME)now.seconds * 1000) + (MS_TIME)now.millis; + } + +#elif defined(TIME_OVERRIDES) +#ifndef NO_ASN + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME) wc_Time(0) * 1000; + } +#else + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME) XTIME(0) * 1000; + } +#endif + +#elif defined(XTIME_MS) + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)XTIME_MS(0); + } + +#elif defined(USE_WINDOWS_API) + MS_TIME TimeNowInMilliseconds(void) + { + static int init = 0; + static LARGE_INTEGER freq; + LARGE_INTEGER count; + + if (!init) { + QueryPerformanceFrequency(&freq); + init = 1; + } + + QueryPerformanceCounter(&count); + + return (MS_TIME)(count.QuadPart / (freq.QuadPart / 1000)); + } + +#elif defined(HAVE_RTP_SYS) + #include "rtptime.h" + + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)rtp_get_system_sec() * 1000; + } +#elif defined(WOLFSSL_DEOS) + MS_TIME TimeNowInMilliseconds(void) + { + const word32 systemTickTimeInHz = 1000000 / systemTickInMicroseconds(); + word32 *systemTickPtr = systemTickPointer(); + + return (MS_TIME) (*systemTickPtr/systemTickTimeInHz) * 1000; + } +#elif defined(MICRIUM) + MS_TIME TimeNowInMilliseconds(void) + { + OS_TICK ticks = 0; + OS_ERR err; + + ticks = OSTimeGet(&err); + + return (MS_TIME) (ticks / OSCfg_TickRate_Hz) * 1000; + } +#elif defined(MICROCHIP_TCPIP_V5) + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME) (TickGet() / (TICKS_PER_SECOND / 1000)); + } +#elif defined(MICROCHIP_TCPIP) + #if defined(MICROCHIP_MPLAB_HARMONY) + #include + + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)SYS_TMR_TickCountGet() / + (SYS_TMR_TickCounterFrequencyGet() / 1000); + } + #else + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)SYS_TICK_Get() / (SYS_TICK_TicksPerSecondGet() / 1000); + } + #endif + +#elif defined(FREESCALE_MQX) || defined(FREESCALE_KSDK_MQX) + MS_TIME TimeNowInMilliseconds(void) + { + TIME_STRUCT mqxTime; + + _time_get_elapsed(&mqxTime); + + return (MS_TIME) mqxTime.SECONDS * 1000; + } +#elif defined(FREESCALE_FREE_RTOS) || defined(FREESCALE_KSDK_FREERTOS) + #include "include/task.h" + + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)((uint64_t)xTaskGetTickCount() * 1000 / + configTICK_RATE_HZ); + } +#elif defined(FREESCALE_KSDK_BM) + #include "lwip/sys.h" /* lwIP */ + + MS_TIME TimeNowInMilliseconds(void) + { + return sys_now(); + } + +#elif defined(WOLFSSL_CMSIS_RTOS) || defined(WOLFSSL_CMSIS_RTOSv2) + + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)osKernelGetTickCount(); + } + +#elif defined(WOLFSSL_TIRTOS) + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME) Seconds_get() * 1000; + } +#elif defined(WOLFSSL_UTASKER) + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)(uTaskerSystemTick / (TICK_RESOLUTION / 1000)); + } +#elif defined(WOLFSSL_LINUXKM) +#ifdef WOLFSSL_32BIT_MILLI_TIME + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)wc_linuxkm_time_epoch_msecs(); + } +#else + MS_TIME TimeNowInMilliseconds(void) + { + s64 t; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) + struct timespec ts; + getnstimeofday(&ts); + t = ts.tv_sec * (s64)1000; + t += ts.tv_nsec / (s64)1000000; +#else + struct timespec64 ts; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) + ts = current_kernel_time64(); +#else + ktime_get_coarse_real_ts64(&ts); +#endif + t = ts.tv_sec * 1000L; + t += ts.tv_nsec / 1000000L; +#endif + return (MS_TIME)t; + } +#endif +#elif defined(WOLFSSL_QNX_CAAM) + MS_TIME TimeNowInMilliseconds(void) + { + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + return (MS_TIME)(now.tv_sec * 1000 + now.tv_nsec / 1000000); + } +#elif defined(FUSION_RTOS) + MS_TIME TimeNowInMilliseconds(void) + { + struct timeval now; + if (FCL_GETTIMEOFDAY(&now, 0) < 0) + return 0; + + /* Convert to milliseconds number. */ + return (MS_TIME)((MS_TIME)now.tv_sec * 1000 + now.tv_usec / 1000); + } +#elif defined(WOLFSSL_ZEPHYR) + MS_TIME TimeNowInMilliseconds(void) + { + int64_t t; + #if defined(CONFIG_ARCH_POSIX) + k_cpu_idle(); + #endif + t = k_uptime_get(); /* returns current uptime in milliseconds */ + return (MS_TIME)t; + } +#elif defined(FREERTOS) + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)((uint64_t)xTaskGetTickCount() * 1000 / + configTICK_RATE_HZ); + } +#else + MS_TIME TimeNowInMilliseconds(void) + { + struct timeval now; + + if (gettimeofday(&now, 0) < 0) + return 0; + + /* Convert to milliseconds number. */ + return (MS_TIME)((MS_TIME)now.tv_sec * 1000 + now.tv_usec / 1000); + } +#endif +#undef MS_TIME +#else + /* The application supplies TimeNowInMilliseconds(). */ +#endif /* !NO_ASN_TIME */ +#endif /* !NO_TLS && WOLFSSL_TLS13 && (HAVE_SESSION_TICKET || !NO_PSK || + * WOLFSSL_DTLS13) */ + #ifndef NO_TLS #if !defined(WOLFSSL_NO_CLIENT_AUTH) && \ diff --git a/src/tls13.c b/src/tls13.c index bf6b3ce8061..524e364fb2b 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -124,12 +124,6 @@ #include #endif -#if defined(__MACH__) || defined(__FreeBSD__) || \ - defined(__INCLUDE_NUTTX_CONFIG_H) || defined(WOLFSSL_RIOT_OS) -#include -#endif /* __MACH__ || __FreeBSD__ || - __INCLUDE_NUTTX_CONFIG_H || WOLFSSL_RIOT_OS */ - #include #include @@ -2103,614 +2097,6 @@ int DeriveTls13Keys(WOLFSSL* ssl, int secret, int side, int store) return ret; } -#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) || defined(WOLFSSL_DTLS13) -#ifdef WOLFSSL_32BIT_MILLI_TIME -#ifndef NO_ASN_TIME -#if defined(USER_TICKS) -#if 0 - word32 TimeNowInMilliseconds(void) - { - /* - write your own clock tick function if don't want gettimeofday() - needs millisecond accuracy but doesn't have to correlated to EPOCH - */ - } -#endif - -#elif defined(WOLFSSL_OPTEE) - word32 TimeNowInMilliseconds(void) - { - TEE_Time now; - - /* Monotonic within the TA, and not the normal world's clock. */ - TEE_GetSystemTime(&now); - - return (word32)((now.seconds * 1000) + now.millis); - } - -#elif defined(TIME_OVERRIDES) -#if !defined(NO_ASN) && !defined(NO_ASN_TIME) - word32 TimeNowInMilliseconds(void) - { - return (word32) wc_Time(0) * 1000; - } -#else - #ifndef HAVE_TIME_T_TYPE - typedef long time_t; - #endif - extern time_t XTIME(time_t * timer); - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (word32) XTIME(0) * 1000; - } -#endif - -#elif defined(XTIME_MS) - word32 TimeNowInMilliseconds(void) - { - return (word32)XTIME_MS(0); - } - -#elif defined(USE_WINDOWS_API) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - static int init = 0; - static LARGE_INTEGER freq; - LARGE_INTEGER count; - - if (!init) { - QueryPerformanceFrequency(&freq); - init = 1; - } - - QueryPerformanceCounter(&count); - - return (word32)(count.QuadPart / (freq.QuadPart / 1000)); - } - -#elif defined(HAVE_RTP_SYS) - #include "rtptime.h" - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (word32)rtp_get_system_sec() * 1000; - } -#elif defined(WOLFSSL_DEOS) - word32 TimeNowInMilliseconds(void) - { - const word32 systemTickTimeInHz = 1000000 / systemTickInMicroseconds(); - word32 *systemTickPtr = systemTickPointer(); - - return (word32) (*systemTickPtr/systemTickTimeInHz) * 1000; - } -#elif defined(MICRIUM) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - OS_TICK ticks = 0; - OS_ERR err; - - ticks = OSTimeGet(&err); - - return (word32) (ticks / OSCfg_TickRate_Hz) * 1000; - } -#elif defined(MICROCHIP_TCPIP_V5) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (word32) (TickGet() / (TICKS_PER_SECOND / 1000)); - } -#elif defined(MICROCHIP_TCPIP) - #if defined(MICROCHIP_MPLAB_HARMONY) - #include - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (word32)(SYS_TMR_TickCountGet() / - (SYS_TMR_TickCounterFrequencyGet() / 1000)); - } - #else - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (word32)(SYS_TICK_Get() / (SYS_TICK_TicksPerSecondGet() / 1000)); - } - - #endif - -#elif defined(FREESCALE_MQX) || defined(FREESCALE_KSDK_MQX) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - TIME_STRUCT mqxTime; - - _time_get_elapsed(&mqxTime); - - return (word32) mqxTime.SECONDS * 1000; - } -#elif defined(FREESCALE_FREE_RTOS) || defined(FREESCALE_KSDK_FREERTOS) - #include "include/task.h" - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (word32)((uint64_t)xTaskGetTickCount() * 1000 / - configTICK_RATE_HZ); - } -#elif defined(FREESCALE_KSDK_BM) - #include "lwip/sys.h" /* lwIP */ - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return sys_now(); - } - -#elif defined(WOLFSSL_CMSIS_RTOS) || defined(WOLFSSL_CMSIS_RTOSv2) - - word32 TimeNowInMilliseconds(void) - { - return (word32)osKernelGetTickCount(); - } - -#elif defined(WOLFSSL_TIRTOS) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (word32) Seconds_get() * 1000; - } -#elif defined(WOLFSSL_UTASKER) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (word32)(uTaskerSystemTick / (TICK_RESOLUTION / 1000)); - } -#elif defined(WOLFSSL_LINUXKM) - word32 TimeNowInMilliseconds(void) - { - return (word32)wc_linuxkm_time_epoch_msecs(); - } -#elif defined(WOLFSSL_QNX_CAAM) - word32 TimeNowInMilliseconds(void) - { - struct timespec now; - clock_gettime(CLOCK_REALTIME, &now); - return (word32)(now.tv_sec * 1000 + now.tv_nsec / 1000000); - } -#elif defined(FUSION_RTOS) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - struct timeval now; - if (FCL_GETTIMEOFDAY(&now, 0) < 0) - return 0; - - /* Convert to milliseconds number. */ - return (word32)(now.tv_sec * 1000 + now.tv_usec / 1000); - } -#elif defined(WOLFSSL_ZEPHYR) - word32 TimeNowInMilliseconds(void) - { - int64_t t; - #if defined(CONFIG_ARCH_POSIX) - k_cpu_idle(); - #endif - t = k_uptime_get(); /* returns current uptime in milliseconds */ - return (word32)t; - } -#elif defined(FREERTOS) - word32 TimeNowInMilliseconds(void) - { - return (word32)((uint64_t)xTaskGetTickCount() * 1000 / - configTICK_RATE_HZ); - } -#else - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - struct timeval now; - - if (gettimeofday(&now, 0) < 0) - return 0; - - /* Convert to milliseconds number. */ - return (word32)(now.tv_sec * 1000 + now.tv_usec / 1000); - } -#endif -#else - /* user must supply time in milliseconds function: - * word32 TimeNowInMilliseconds(void); - * The response is milliseconds elapsed - */ -#endif /* !NO_ASN_TIME */ -#else -#ifndef NO_ASN_TIME -#if defined(USER_TICKS) -#if 0 - sword64 TimeNowInMilliseconds(void) - { - /* - write your own clock tick function if don't want gettimeofday() - needs millisecond accuracy but doesn't have to correlated to EPOCH - */ - } -#endif - -#elif defined(WOLFSSL_OPTEE) - sword64 TimeNowInMilliseconds(void) - { - TEE_Time now; - - /* Monotonic within the TA, and not the normal world's clock. */ - TEE_GetSystemTime(&now); - - return ((sword64)now.seconds * 1000) + (sword64)now.millis; - } - -#elif defined(TIME_OVERRIDES) -#if !defined(NO_ASN) && !defined(NO_ASN_TIME) - sword64 TimeNowInMilliseconds(void) - { - return (sword64) wc_Time(0) * 1000; - } -#else - #ifndef HAVE_TIME_T_TYPE - typedef long time_t; - #endif - extern time_t XTIME(time_t * timer); - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64) XTIME(0) * 1000; - } -#endif - -#elif defined(XTIME_MS) - sword64 TimeNowInMilliseconds(void) - { - return (sword64)XTIME_MS(0); - } - -#elif defined(USE_WINDOWS_API) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - static int init = 0; - static LARGE_INTEGER freq; - LARGE_INTEGER count; - - if (!init) { - QueryPerformanceFrequency(&freq); - init = 1; - } - - QueryPerformanceCounter(&count); - - return (sword64)(count.QuadPart / (freq.QuadPart / 1000)); - } - -#elif defined(HAVE_RTP_SYS) - #include "rtptime.h" - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64)rtp_get_system_sec() * 1000; - } -#elif defined(WOLFSSL_DEOS) - sword64 TimeNowInMilliseconds(void) - { - const word32 systemTickTimeInHz = 1000000 / systemTickInMicroseconds(); - word32 *systemTickPtr = systemTickPointer(); - - return (sword64) (*systemTickPtr/systemTickTimeInHz) * 1000; - } -#elif defined(MICRIUM) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - OS_TICK ticks = 0; - OS_ERR err; - - ticks = OSTimeGet(&err); - - return (sword64) (ticks / OSCfg_TickRate_Hz) * 1000; - } -#elif defined(MICROCHIP_TCPIP_V5) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64) (TickGet() / (TICKS_PER_SECOND / 1000)); - } -#elif defined(MICROCHIP_TCPIP) - #if defined(MICROCHIP_MPLAB_HARMONY) - #include - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64)SYS_TMR_TickCountGet() / - (SYS_TMR_TickCounterFrequencyGet() / 1000); - } - #else - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64)SYS_TICK_Get() / (SYS_TICK_TicksPerSecondGet() / 1000); - } - - #endif - -#elif defined(FREESCALE_MQX) || defined(FREESCALE_KSDK_MQX) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - TIME_STRUCT mqxTime; - - _time_get_elapsed(&mqxTime); - - return (sword64) mqxTime.SECONDS * 1000; - } -#elif defined(FREESCALE_FREE_RTOS) || defined(FREESCALE_KSDK_FREERTOS) - #include "include/task.h" - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64)((uint64_t)xTaskGetTickCount() * 1000 / - configTICK_RATE_HZ); - } -#elif defined(FREESCALE_KSDK_BM) - #include "lwip/sys.h" /* lwIP */ - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return sys_now(); - } - -#elif defined(WOLFSSL_CMSIS_RTOS) || defined(WOLFSSL_CMSIS_RTOSv2) - - sword64 TimeNowInMilliseconds(void) - { - return (sword64)osKernelGetTickCount(); - } - -#elif defined(WOLFSSL_TIRTOS) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64) Seconds_get() * 1000; - } -#elif defined(WOLFSSL_UTASKER) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64)(uTaskerSystemTick / (TICK_RESOLUTION / 1000)); - } -#elif defined(WOLFSSL_LINUXKM) - sword64 TimeNowInMilliseconds(void) - { - s64 t; -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) - struct timespec ts; - getnstimeofday(&ts); - t = ts.tv_sec * (s64)1000; - t += ts.tv_nsec / (s64)1000000; -#else - struct timespec64 ts; -#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) - ts = current_kernel_time64(); -#else - ktime_get_coarse_real_ts64(&ts); -#endif - t = ts.tv_sec * 1000L; - t += ts.tv_nsec / 1000000L; -#endif - return (sword64)t; - } -#elif defined(WOLFSSL_QNX_CAAM) - sword64 TimeNowInMilliseconds(void) - { - struct timespec now; - clock_gettime(CLOCK_REALTIME, &now); - return (sword64)(now.tv_sec * 1000 + now.tv_nsec / 1000000); - } -#elif defined(FUSION_RTOS) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - struct timeval now; - if (FCL_GETTIMEOFDAY(&now, 0) < 0) - return 0; - - /* Convert to milliseconds number. */ - return (sword64)now.tv_sec * 1000 + now.tv_usec / 1000; - } -#elif defined(WOLFSSL_ZEPHYR) - sword64 TimeNowInMilliseconds(void) - { - int64_t t; - #if defined(CONFIG_ARCH_POSIX) - k_cpu_idle(); - #endif - t = k_uptime_get(); /* returns current uptime in milliseconds */ - return (sword64)t; - } -#elif defined(FREERTOS) - sword64 TimeNowInMilliseconds(void) - { - return (sword64)((uint64_t)xTaskGetTickCount() * 1000 / - configTICK_RATE_HZ); - } -#else - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - struct timeval now; - - if (gettimeofday(&now, 0) < 0) - return 0; - - /* Convert to milliseconds number. */ - return (sword64)now.tv_sec * 1000 + now.tv_usec / 1000; - } -#endif -#else - /* user must supply time in milliseconds function: - * sword64 TimeNowInMilliseconds(void); - * The response is milliseconds elapsed - */ -#endif /* !NO_ASN_TIME */ -#endif /* WOLFSSL_32BIT_MILLI_TIME */ -#endif /* HAVE_SESSION_TICKET || !NO_PSK || WOLFSSL_DTLS13 */ - /* Add record layer header to message. * * output The buffer to write the record layer header into. From 0bace31316ebdfdbe93c9c1ad49ffd66cd15e6a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Tue, 3 Nov 2026 11:29:15 +0100 Subject: [PATCH 3/3] Implement DTLSv1_get_timeout and DTLSv1_handle_timeout wolfSSL_DTLSv1_get_timeout always returned 0 and reported the full timeout instead of the time left, and wolfSSL_DTLSv1_handle_timeout was a stub that did nothing. OpenSSL style event loops that wait for get_timeout and then call handle_timeout never retransmitted a lost flight. Track two timers on the object. The retransmission timer restarts whenever a handshake message is stored for retransmission (each DTLS 1.2 pool save and DTLS 1.3 retransmit record), whenever the flight is resent, either on a timeout or because the peer retransmitted or acked part of it, including a resend that hit WANT_WRITE, on every timeout backoff, and when a valid handshake message, or in DTLS 1.3 an ACK that leaves nothing to retransmit, resets a backed off timeout to its initial value. Restarting on every stored message rather than only on an empty list keeps a renegotiation or post-handshake flight from inheriting the start time of an older flight that is still kept. A DTLS 1.2 flight counts as sent only in the states DtlsMsgPoolSend accepts for a retransmission, so no timer runs while a flight is still being written or waits for an asynchronous operation. A DTLS 1.3 flight counts as sent while records are kept for it. get_timeout and handle_timeout report no retransmission timer while the handshake is suspended on asynchronous crypto, non-blocking OCSP or a chain verify callback, so a slow signature cannot trigger a retransmission of a partly sent flight. The DTLS 1.3 ack timer runs for a quarter of the timeout while received records wait for an ack that can be sent. It starts when the first of those records is seen and whenever the quick timeout flag is set again. DtlsTimersRunning() reports which timer runs and replaces the inline check in EmbedReceiveFrom, which keeps its behaviour. The timers use TimeNowInMilliseconds, which is now also built for DTLS, so DTLS 1.2 builds without session tickets, PSK or TLS 1.3 no longer count whole seconds. Whole seconds stay the fallback where TLS 1.3 tickets, PSK and DTLS 1.3 do not require a millisecond clock. With USER_TICKS, or on a platform that would land in the gettimeofday() default without HAVE_GETTIMEOFDAY, the library defines no TimeNowInMilliseconds and the timers count LowResTimer seconds instead. Ports with only a seconds clock, such as Xilinx or Nucleus, keep building, and a USER_TICKS application that supplies its own TimeNowInMilliseconds still links. The DEOS clock now reads the tick counter through a const volatile pointer like the DEOS LowResTimer, since DTLS 1.2 builds now compile it. The Zephyr clock skips k_cpu_idle() on native_posix and native_sim like the Zephyr LowResTimer, since idling there advances simulated time and the DTLS timers now read this clock. CMake now exports HAVE_GETTIMEOFDAY to config.h as autotools does. The ladder marks its whole second branches and DtlsTimerLowRes reports that to the timers, so internal.h needs no second copy of the ladder. With a whole second clock the timers round elapsed time down by one tick so a timer never expires early. The ack timer is not rounded down, since an early ACK only makes the peer resend the records it is missing. A timeout of 0 means no timeout, so no timer runs. get_timeout reports at most DTLS_TIMEOUT_MAX seconds, which also keeps the millisecond count from overflowing. DtlsTimerNowMs and DtlsTimerLowRes are visible to the tests and get WOLFSSL_API_PREFIX_MAP aliases. Fix the DTLS 1.3 quick timeout. Dtls13RtxTimeout cleared the flag when it sent the ACKs and then checked it, so a timeout taken after the quarter wait also retransmitted the flight and doubled the timeout. Dtls13RtxTimeout now takes the timer that expired. handle_timeout passes the timer it found expired. The receive path and wolfSSL_dtls_got_timeout do not know how long the caller waited, so with the quick timeout flag set they send only the ACKs while the buffered flight has waited less than half its timeout, and retransmit with backoff once it waited longer. Without a clock a quick timeout only sends the ACKs and the next full timeout retransmits. A quick wait stays under half the timeout and a full one reaches it. A whole second clock is rounded down as for the timers, so a full wait can read as a quick one and retransmit one timeout later. With nothing buffered to retransmit, a timeout with the flag set only sends the ACKs instead of backing off. Flushing the seen records also clears the flag, so a flag left over from the previous flight no longer turns the next full timeout into an ACK-only one. A timeout within DTLS13_MIN_RTX_INTERVAL of the last retransmission no longer doubles the timeout when Dtls13RtxSendBuffered holds the retransmission back. Dtls13RtxSendBuffered now uses Dtls13RtxWaitMs for that check, and a clock that stepped back counts as elapsed, so it no longer holds the retransmission back until the clock catches up. test_dtls_timeout clears the last retransmission time before its second timeout, which falls within that interval of the first one. test_dtls_drop_client_ack takes one timeout instead of two, since the first one no longer finds a flag left over from the handshake. get_timeout now returns 1 with the time left while a timer runs and 0 otherwise, and no longer fills in dtls_timeout when no timer runs, so callers must check the return value. The time left includes any wait before DTLS 1.3 may retransmit again. No timer runs while the object's own output is still pending, so handle_timeout cannot queue a second copy of a blocked write. handle_timeout returns 0 when no timer expired. For an expired retransmission timer it sends any pending ACKs and retransmits with backoff, and returns 1. For an expired DTLS 1.3 ack timer it only sends the ACKs, without retransmitting or backing off, and returns 1. It returns -1 on error, including when the timeout reached its maximum, the ACK hits WANT_WRITE or the timers cannot be read under WOLFSSL_RW_THREADED, which also sets the error, while get_timeout returns 0. At the maximum timeout DTLS 1.3 now sets SOCKET_ERROR_E, as DTLS 1.2 does, instead of -1. Both return 0 when NO_ASN_TIME leaves no clock. Under WOLFSSL_RW_THREADED the DTLS 1.3 ack timer state now sits behind the dtls13Rtx mutex. The read thread sets the ack timer start and the quick timeout flag with it held, the timeout handler reads and clears the flag and the pending ACK state with it held, and get_timeout reads the start in the same locked section as the flag, so it never pairs a set flag with a stale start. --- cmake/config.in | 3 + src/dtls13.c | 133 +++++-- src/internal.c | 175 +++++++-- src/ssl_api_dtls.c | 171 ++++++-- src/wolfio.c | 21 +- tests/api/test_dtls.c | 885 +++++++++++++++++++++++++++++++++++++++++- tests/api/test_dtls.h | 2 + wolfssl/internal.h | 28 +- 8 files changed, 1315 insertions(+), 103 deletions(-) diff --git a/cmake/config.in b/cmake/config.in index 295581bbdc2..209899181fd 100644 --- a/cmake/config.in +++ b/cmake/config.in @@ -19,6 +19,9 @@ /* Define to 1 if you have the `getaddrinfo' function. */ #cmakedefine HAVE_GETADDRINFO @HAVE_GETADDRINFO@ +/* Define to 1 if you have the `gettimeofday' function. */ +#cmakedefine HAVE_GETTIMEOFDAY @HAVE_GETTIMEOFDAY@ + /* Define to 1 if you have the `gmtime_r' function. */ #cmakedefine HAVE_GMTIME_R @HAVE_GMTIME_R@ diff --git a/src/dtls13.c b/src/dtls13.c index f1a9afd7f4d..0a1ebf89d12 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -782,6 +782,10 @@ int Dtls13RtxAddAck(WOLFSSL* ssl, w64wrapper epoch, w64wrapper seq) return MEMORY_E; } + #ifndef NO_ASN_TIME + if (ssl->dtls13Rtx.seenRecords == NULL) + ssl->dtls_ack_timer_start = DtlsTimerNowMs(); + #endif *prevNext = rn; rn->next = cur; ssl->dtls13Rtx.seenRecordsCount++; @@ -827,6 +831,7 @@ static void Dtls13RtxFlushAcks(WOLFSSL* ssl) ssl->dtls13Rtx.seenRecords = NULL; ssl->dtls13Rtx.seenRecordsCount = 0; + ssl->dtls13FastTimeout = 0; #ifdef WOLFSSL_RW_THREADED wc_UnLockMutex(&ssl->dtls13Rtx.mutex); #endif @@ -1039,8 +1044,10 @@ static int Dtls13SendOneFragmentRtx(WOLFSSL* ssl, handshakeType, hashOutput, Dtls13SendNow(ssl, handshakeType)); if (rtxRecord != NULL) { - if (ret == 0 || ret == WC_NO_ERR_TRACE(WANT_WRITE)) + if (ret == 0 || ret == WC_NO_ERR_TRACE(WANT_WRITE)) { Dtls13RtxAddRecord(&ssl->dtls13Rtx, rtxRecord); + DtlsTimerStart(ssl); + } else Dtls13FreeRtxBufferRecord(ssl, rtxRecord); } @@ -1628,8 +1635,20 @@ int Dtls13RecordRecvd(WOLFSSL* ssl) if (ssl->curRL.type != handshake) return 0; - if (!ssl->options.dtls13SendMoreAcks) + if (!ssl->options.dtls13SendMoreAcks) { + #ifdef WOLFSSL_RW_THREADED + if (wc_LockMutex(&ssl->dtls13Rtx.mutex) != 0) + return BAD_MUTEX_E; + #endif + #ifndef NO_ASN_TIME + if (!ssl->dtls13FastTimeout) + ssl->dtls_ack_timer_start = DtlsTimerNowMs(); + #endif ssl->dtls13FastTimeout = 1; + #ifdef WOLFSSL_RW_THREADED + wc_UnLockMutex(&ssl->dtls13Rtx.mutex); + #endif + } /* Acking happens in Dtls13RtxAddAckForCurRecord(). */ return 0; @@ -1655,26 +1674,18 @@ static int Dtls13RtxSendBuffered(WOLFSSL* ssl) byte* output; int isLast; int sendSz; -#ifndef NO_ASN_TIME -#ifdef WOLFSSL_32BIT_MILLI_TIME - word32 now; -#else - sword64 now; -#endif -#endif int ret; WOLFSSL_ENTER("Dtls13RtxSendBuffered"); #ifndef NO_ASN_TIME - now = TimeNowInMilliseconds(); - if (now - ssl->dtls13Rtx.lastRtx < DTLS13_MIN_RTX_INTERVAL) { + if (Dtls13RtxWaitMs(ssl) > 0) { #ifdef WOLFSSL_DEBUG_TLS WOLFSSL_MSG("Avoid too fast retransmission"); #endif /* WOLFSSL_DEBUG_TLS */ return 0; } - ssl->dtls13Rtx.lastRtx = now; + ssl->dtls13Rtx.lastRtx = TimeNowInMilliseconds(); #endif r = ssl->dtls13Rtx.rtxRecords; @@ -1731,6 +1742,7 @@ static int Dtls13RtxSendBuffered(WOLFSSL* ssl) /* this fragment will be sent eventually. Move it to the end of the list so next time we start with a new one. */ Dtls13RtxMoveToEndOfList(ssl, prevNext, r); + DtlsTimerStart(ssl); return ret; } @@ -1738,6 +1750,7 @@ static int Dtls13RtxSendBuffered(WOLFSSL* ssl) r = r->next; } + DtlsTimerStart(ssl); return 0; } @@ -3418,6 +3431,7 @@ int Dtls13DoScheduledWork(WOLFSSL* ssl) *tail = ssl->dtls13Rtx.seenRecords; ssl->dtls13Rtx.seenRecords = NULL; ssl->dtls13Rtx.seenRecordsCount = 0; + ssl->dtls13FastTimeout = 0; } else { /* Queue already at the cap: drop this batch. */ @@ -3456,39 +3470,89 @@ int Dtls13DoScheduledWork(WOLFSSL* ssl) return 0; } -/* Send ACKs when available after a timeout but only retransmit the last - * flight after a long timeout */ -int Dtls13RtxTimeout(WOLFSSL* ssl) +/* Return 1 when seen records wait for an ACK that may be sent now. */ +int Dtls13AckSendable(const WOLFSSL* ssl) +{ + return ssl->dtls13Rtx.seenRecords != NULL && + (ssl->options.serverState >= SERVER_HELLO_COMPLETE || + ssl->options.seenUnifiedHdr); +} + +/* Milliseconds until Dtls13RtxSendBuffered() retransmits again. */ +word32 Dtls13RtxWaitMs(WOLFSSL* ssl) +{ +#ifndef NO_ASN_TIME +#ifdef WOLFSSL_32BIT_MILLI_TIME + word32 since = TimeNowInMilliseconds() - ssl->dtls13Rtx.lastRtx; +#else + sword64 since = TimeNowInMilliseconds() - ssl->dtls13Rtx.lastRtx; + + if (since < 0) + return 0; +#endif + + if (since < DTLS13_MIN_RTX_INTERVAL) + return (word32)(DTLS13_MIN_RTX_INTERVAL - since); +#else + (void)ssl; +#endif + return 0; +} + +/* Return 1 once the buffered flight waited half its retransmission timeout. */ +static int Dtls13RtxTimerDue(WOLFSSL* ssl) +{ +#ifndef NO_ASN_TIME + if (ssl->dtls13Rtx.rtxRecords == NULL || ssl->dtls_timeout <= 0) + return 0; + return DtlsTimerElapsedMs(ssl->dtls_timer_start) >= + DtlsTimerTimeoutMs(ssl) / 2; +#else + (void)ssl; + return 0; +#endif +} + +/* Send ACKs after a timeout and retransmit the last flight only after a long + * timeout. expired is the DtlsTimer that expired, or 0 when not known. */ +int Dtls13RtxTimeout(WOLFSSL* ssl, int expired) { int ret = 0; + int ackOnly = (expired == DTLS_TIMER_ACK); + int fastTimeout; + int sendAck; - /* We don't want to send acks until we have done version - * negotiation in the SH or have received a unified header in the - * DTLS record. */ - if (ssl->dtls13Rtx.seenRecords != NULL && - (ssl->options.serverState >= SERVER_HELLO_COMPLETE || - ssl->options.seenUnifiedHdr)) { +#ifdef WOLFSSL_RW_THREADED + if (wc_LockMutex(&ssl->dtls13Rtx.mutex) != 0) + return BAD_MUTEX_E; +#endif + fastTimeout = ssl->dtls13FastTimeout; + ssl->dtls13FastTimeout = 0; + sendAck = Dtls13AckSendable(ssl); + if (sendAck) ssl->dtls13Rtx.sendAcks = 0; - /* reset fast timeout as we are sending ACKs */ - ssl->dtls13FastTimeout = 0; +#ifdef WOLFSSL_RW_THREADED + wc_UnLockMutex(&ssl->dtls13Rtx.mutex); +#endif + + if (expired == 0 && fastTimeout) + ackOnly = !Dtls13RtxTimerDue(ssl); + + if (sendAck) { ret = SendDtls13Ack(ssl); if (ret != 0) return ret; } - /* we have two timeouts, a shorter (dtls13FastTimeout = 1) and a longer - one. When the shorter expires we only send ACKs, as it normally means - that some messages we are waiting for don't arrive yet. But we - retransmit our buffered messages only if the longer timeout - expires. fastTimeout is 1/4 of the longer timeout */ - if (ssl->dtls13FastTimeout) { - ssl->dtls13FastTimeout = 0; + if (ackOnly) + return 0; + + if (Dtls13RtxWaitMs(ssl) > 0) return 0; - } /* Increase timeout on long timeout */ if (DtlsMsgPoolTimeout(ssl) != 0) - return WOLFSSL_FATAL_ERROR; + return SOCKET_ERROR_E; return Dtls13RtxSendBuffered(ssl); } @@ -3623,6 +3687,7 @@ int DoDtls13Ack(WOLFSSL* ssl, const byte* input, word32 inputSize, #ifndef WOLFSSL_RW_THREADED int ret; #endif + int hadRecords; int i; /* an epoch 0 ACK once encrypted only triggers retransmission */ @@ -3646,6 +3711,7 @@ int DoDtls13Ack(WOLFSSL* ssl, const byte* input, word32 inputSize, WOLFSSL_MSG("read ack records"); + hadRecords = ssl->dtls13Rtx.rtxRecords != NULL; ackMessage = input + OPAQUE16_LEN; for (i = 0; i < length; i += DTLS13_RN_SIZE) { ato64(ackMessage + i, &epoch); @@ -3670,6 +3736,9 @@ int DoDtls13Ack(WOLFSSL* ssl, const byte* input, word32 inputSize, #endif /* HAVE_WRITE_DUP */ } + if (hadRecords && ssl->dtls13Rtx.rtxRecords == NULL) + DtlsTimeoutReset(ssl); + /* last client flight was completely acknowledged by the server. Handshake is complete. */ if (ssl->options.side == WOLFSSL_CLIENT_END && diff --git a/src/internal.c b/src/internal.c index 4d04525fece..0e4bf6d496b 100644 --- a/src/internal.c +++ b/src/internal.c @@ -11934,6 +11934,7 @@ int DtlsMsgPoolSave(WOLFSSL* ssl, const byte* data, word32 dataSz, cur->next = item; } ssl->dtls_tx_msg_list_sz++; + DtlsTimerStart(ssl); } else ret = MEMORY_E; @@ -11950,12 +11951,111 @@ int DtlsMsgPoolTimeout(WOLFSSL* ssl) WOLFSSL_ENTER("DtlsMsgPoolTimeout"); if (ssl->dtls_timeout < ssl->dtls_timeout_max) { ssl->dtls_timeout *= DTLS_TIMEOUT_MULTIPLIER; + DtlsTimerStart(ssl); result = 0; } WOLFSSL_LEAVE("DtlsMsgPoolTimeout()", result); return result; } +#ifndef NO_ASN_TIME +/* Milliseconds since start, never more than really passed. */ +word32 DtlsTimerElapsedMs(word32 start) +{ + word32 elapsed = DtlsTimerNowMs() - start; + + /* Both readings were truncated to whole seconds. */ + if (DtlsTimerLowRes()) + elapsed = (elapsed >= 1000) ? elapsed - 1000 : 0; + return elapsed; +} + +/* Current retransmission timeout in milliseconds. */ +word32 DtlsTimerTimeoutMs(const WOLFSSL* ssl) +{ + return (word32)min(ssl->dtls_timeout, DTLS_TIMEOUT_MAX) * 1000; +} +#endif /* !NO_ASN_TIME */ + +/* Start or restart the retransmission timer. */ +void DtlsTimerStart(WOLFSSL* ssl) +{ +#ifndef NO_ASN_TIME + ssl->dtls_timer_start = DtlsTimerNowMs(); +#else + (void)ssl; +#endif +} + +/* Reset a backed off timeout and restart the timer with it. */ +void DtlsTimeoutReset(WOLFSSL* ssl) +{ + if (ssl->dtls_timeout != ssl->dtls_timeout_init) { + ssl->dtls_timeout = ssl->dtls_timeout_init; + DtlsTimerStart(ssl); + } +} + +/* Return 1 when the stored flight was sent and waits for the peer. */ +static int DtlsMsgPoolWaiting(WOLFSSL* ssl) +{ + if (ssl->options.side == WOLFSSL_SERVER_END) { + return ssl->options.acceptState == ACCEPT_BEGIN_RENEG || + ssl->options.acceptState == SERVER_HELLO_DONE || + ssl->options.acceptState == ACCEPT_FINISHED_DONE || + ssl->options.acceptState == ACCEPT_THIRD_REPLY_DONE; + } + if (ssl->options.side == WOLFSSL_CLIENT_END) { + return ssl->options.connectState == CLIENT_HELLO_SENT || + ssl->options.connectState == HELLO_AGAIN_REPLY || + ssl->options.connectState == FINISHED_DONE || + ssl->options.connectState == SECOND_REPLY_DONE; + } + return 1; +} + +/* Get the DTLS timers that run. + * + * @param [in] ssl SSL/TLS object. + * @param [out] ackStart Start of the DTLS 1.3 ack timer. May be NULL. + * @return DTLS_TIMER_RTX and DTLS_TIMER_ACK bits, 0 when none runs. + * @return BAD_MUTEX_E when locking fails. + */ +int DtlsTimersRunning(WOLFSSL* ssl, word32* ackStart) +{ + int running = 0; + + (void)ackStart; + +#ifdef WOLFSSL_DTLS13 + if (IsAtLeastTLSv1_3(ssl->version)) { + if (ssl->dtls13Rtx.rtxRecords != NULL) + running |= DTLS_TIMER_RTX; + #ifdef WOLFSSL_RW_THREADED + if (wc_LockMutex(&ssl->dtls13Rtx.mutex) != 0) + return BAD_MUTEX_E; + #endif + if (ssl->dtls13FastTimeout && Dtls13AckSendable(ssl)) + running |= DTLS_TIMER_ACK; + #ifndef NO_ASN_TIME + if (ackStart != NULL) + *ackStart = ssl->dtls_ack_timer_start; + #endif + #ifdef WOLFSSL_RW_THREADED + wc_UnLockMutex(&ssl->dtls13Rtx.mutex); + #endif + return running; + } +#endif /* WOLFSSL_DTLS13 */ + + if (ssl->options.handShakeState != HANDSHAKE_DONE && + (ssl->dtls_tx_msg_list != NULL || ssl->dtls_tx_msg != NULL) && + DtlsMsgPoolWaiting(ssl)) + running |= DTLS_TIMER_RTX; + + return running; +} + /* DtlsMsgPoolReset() deletes the stored transmit list. */ void DtlsMsgPoolReset(WOLFSSL* ssl) @@ -12047,17 +12147,7 @@ int DtlsMsgPoolSend(WOLFSSL* ssl, int sendOnlyFirstPacket) pool = ssl->dtls_tx_msg == NULL ? ssl->dtls_tx_msg_list : ssl->dtls_tx_msg; if (pool != NULL) { - if ((ssl->options.side == WOLFSSL_SERVER_END && - !(ssl->options.acceptState == ACCEPT_BEGIN_RENEG || - ssl->options.acceptState == SERVER_HELLO_DONE || - ssl->options.acceptState == ACCEPT_FINISHED_DONE || - ssl->options.acceptState == ACCEPT_THIRD_REPLY_DONE)) || - (ssl->options.side == WOLFSSL_CLIENT_END && - !(ssl->options.connectState == CLIENT_HELLO_SENT || - ssl->options.connectState == HELLO_AGAIN_REPLY || - ssl->options.connectState == FINISHED_DONE || - ssl->options.connectState == SECOND_REPLY_DONE))) { - + if (!DtlsMsgPoolWaiting(ssl)) { WOLFSSL_ERROR(DTLS_RETX_OVER_TX); ssl->error = DTLS_RETX_OVER_TX; return WOLFSSL_FATAL_ERROR; @@ -12166,6 +12256,8 @@ int DtlsMsgPoolSend(WOLFSSL* ssl, int sendOnlyFirstPacket) if (ret == 0 && ssl->options.groupMessages) ret = SendBuffered(ssl); + if (ret == 0 || ret == WC_NO_ERR_TRACE(WANT_WRITE)) + DtlsTimerStart(ssl); } WOLFSSL_LEAVE("DtlsMsgPoolSend()", ret); @@ -12481,9 +12573,13 @@ ProtocolVersion MakeDTLSv1_3(void) */ #endif /* !NO_ASN_TIME */ -#if !defined(NO_TLS) && defined(WOLFSSL_TLS13) && \ - (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) || \ - defined(WOLFSSL_DTLS13)) +/* TLS 1.3 tickets and PSK and DTLS 1.3 need a millisecond clock. */ +#if (defined(WOLFSSL_TLS13) && (defined(HAVE_SESSION_TICKET) || \ + !defined(NO_PSK))) || defined(WOLFSSL_DTLS13) + #define MS_TIME_NEEDED +#endif + +#if !defined(NO_TLS) && (defined(MS_TIME_NEEDED) || defined(WOLFSSL_DTLS)) #ifndef NO_ASN_TIME #ifdef WOLFSSL_32BIT_MILLI_TIME #define MS_TIME word32 @@ -12492,7 +12588,9 @@ ProtocolVersion MakeDTLSv1_3(void) #endif /* The time in milliseconds, for ticket ages and the DTLS timers. */ -#if defined(USER_TICKS) +#if defined(USER_TICKS) && !defined(MS_TIME_NEEDED) + #define MS_TIME_FALLBACK +#elif defined(USER_TICKS) /* The application supplies TimeNowInMilliseconds(). */ #elif defined(WOLFSSL_OPTEE) MS_TIME TimeNowInMilliseconds(void) @@ -12506,6 +12604,7 @@ ProtocolVersion MakeDTLSv1_3(void) } #elif defined(TIME_OVERRIDES) + #define MS_TIME_LOW_RES #ifndef NO_ASN MS_TIME TimeNowInMilliseconds(void) { @@ -12543,20 +12642,23 @@ ProtocolVersion MakeDTLSv1_3(void) #elif defined(HAVE_RTP_SYS) #include "rtptime.h" + #define MS_TIME_LOW_RES MS_TIME TimeNowInMilliseconds(void) { return (MS_TIME)rtp_get_system_sec() * 1000; } #elif defined(WOLFSSL_DEOS) + #define MS_TIME_LOW_RES MS_TIME TimeNowInMilliseconds(void) { const word32 systemTickTimeInHz = 1000000 / systemTickInMicroseconds(); - word32 *systemTickPtr = systemTickPointer(); + const volatile word32 *systemTickPtr = systemTickPointer(); return (MS_TIME) (*systemTickPtr/systemTickTimeInHz) * 1000; } #elif defined(MICRIUM) + #define MS_TIME_LOW_RES MS_TIME TimeNowInMilliseconds(void) { OS_TICK ticks = 0; @@ -12588,6 +12690,7 @@ ProtocolVersion MakeDTLSv1_3(void) #endif #elif defined(FREESCALE_MQX) || defined(FREESCALE_KSDK_MQX) + #define MS_TIME_LOW_RES MS_TIME TimeNowInMilliseconds(void) { TIME_STRUCT mqxTime; @@ -12620,6 +12723,7 @@ ProtocolVersion MakeDTLSv1_3(void) } #elif defined(WOLFSSL_TIRTOS) + #define MS_TIME_LOW_RES MS_TIME TimeNowInMilliseconds(void) { return (MS_TIME) Seconds_get() * 1000; @@ -12678,7 +12782,8 @@ ProtocolVersion MakeDTLSv1_3(void) MS_TIME TimeNowInMilliseconds(void) { int64_t t; - #if defined(CONFIG_ARCH_POSIX) + #if defined(CONFIG_ARCH_POSIX) && !defined(CONFIG_BOARD_NATIVE_POSIX) \ + && !defined(CONFIG_BOARD_NATIVE_SIM) k_cpu_idle(); #endif t = k_uptime_get(); /* returns current uptime in milliseconds */ @@ -12690,6 +12795,8 @@ ProtocolVersion MakeDTLSv1_3(void) return (MS_TIME)((uint64_t)xTaskGetTickCount() * 1000 / configTICK_RATE_HZ); } +#elif !defined(HAVE_GETTIMEOFDAY) && !defined(MS_TIME_NEEDED) + #define MS_TIME_FALLBACK #else MS_TIME TimeNowInMilliseconds(void) { @@ -12702,12 +12809,36 @@ ProtocolVersion MakeDTLSv1_3(void) return (MS_TIME)((MS_TIME)now.tv_sec * 1000 + now.tv_usec / 1000); } #endif + +#ifdef WOLFSSL_DTLS +/* Current time for the DTLS timers, in milliseconds. */ +word32 DtlsTimerNowMs(void) +{ +#ifdef MS_TIME_FALLBACK + return LowResTimer() * 1000; +#else + return (word32)TimeNowInMilliseconds(); +#endif +} + +/* Return 1 when the DTLS timer clock counts whole seconds. */ +int DtlsTimerLowRes(void) +{ +#if defined(MS_TIME_LOW_RES) || defined(MS_TIME_FALLBACK) + return 1; +#else + return 0; +#endif +} +#endif #undef MS_TIME +#undef MS_TIME_LOW_RES +#undef MS_TIME_FALLBACK #else /* The application supplies TimeNowInMilliseconds(). */ #endif /* !NO_ASN_TIME */ -#endif /* !NO_TLS && WOLFSSL_TLS13 && (HAVE_SESSION_TICKET || !NO_PSK || - * WOLFSSL_DTLS13) */ +#endif /* !NO_TLS && (MS_TIME_NEEDED || WOLFSSL_DTLS) */ +#undef MS_TIME_NEEDED #ifndef NO_TLS @@ -13320,7 +13451,7 @@ static int wolfSSLReceive(WOLFSSL* ssl, byte* buf, word32 sz) #ifdef WOLFSSL_DTLS #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) { - int rtxRet = Dtls13RtxTimeout(ssl); + int rtxRet = Dtls13RtxTimeout(ssl, 0); if (rtxRet == WC_NO_ERR_TRACE(WANT_WRITE)) { /* Record that this ACK or retransmit still owes a * write, so the next flush sends it. */ @@ -28127,7 +28258,7 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) ) { /* Reset timeout as we have received a valid * DTLS handshake message */ - ssl->dtls_timeout = ssl->dtls_timeout_init; + DtlsTimeoutReset(ssl); } else if (DtlsShouldDropHandshake(ssl, ret)) { DropAndRestartProcessReply(ssl); @@ -28173,7 +28304,7 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) ) { /* Reset timeout as we have received a valid * DTLS handshake message */ - ssl->dtls_timeout = ssl->dtls_timeout_init; + DtlsTimeoutReset(ssl); } else if (DtlsShouldDropHandshake(ssl, ret)) { DropAndRestartProcessReply(ssl); diff --git a/src/ssl_api_dtls.c b/src/ssl_api_dtls.c index 174aa97ff7b..67a8d0e54c2 100644 --- a/src/ssl_api_dtls.c +++ b/src/ssl_api_dtls.c @@ -1195,36 +1195,163 @@ void wolfSSL_dtls13_set_send_more_acks(WOLFSSL* ssl, int value) } #endif /* WOLFSSL_DTLS13 */ -/* Get the time left until the next DTLS timeout. +#ifdef WOLFSSL_DTLS13 +/* Handle a DTLS 1.3 timeout. * - * @param [in] ssl SSL/TLS object. - * @param [out] timeleft Time left until the next timeout. - * @return 0 always. + * @param [in] ssl SSL/TLS object. + * @param [in] expired DtlsTimer that expired, or 0 when not known. + * @return WOLFSSL_SUCCESS on success. + * @return WOLFSSL_FATAL_ERROR on error. */ -int wolfSSL_DTLSv1_get_timeout(WOLFSSL* ssl, WOLFSSL_TIMEVAL* timeleft) +static int Dtls13GotTimeout(WOLFSSL* ssl, int expired) { - if (ssl && timeleft) { - XMEMSET(timeleft, 0, sizeof(WOLFSSL_TIMEVAL)); - timeleft->tv_sec = ssl->dtls_timeout; + int ret = Dtls13RtxTimeout(ssl, expired); + + if (ret < 0) { + if (ret == WC_NO_ERR_TRACE(WANT_WRITE)) + ssl->dtls13SendingAckOrRtx = 1; + ssl->error = ret; + WOLFSSL_ERROR(ret); + return WOLFSSL_FATAL_ERROR; } + + return WOLFSSL_SUCCESS; +} +#endif /* WOLFSSL_DTLS13 */ + +/* Get the time left on the DTLS timers. + * + * @param [in] ssl SSL/TLS object. + * @param [out] left Milliseconds until the first timer expires. + * @param [out] ackOnly 1 when only the DTLS 1.3 ack timer expired. + * @return 1 when a timer runs. + * @return 0 when no timer runs. + * @return BAD_MUTEX_E when locking fails. + */ +static int DtlsTimerLeft(WOLFSSL* ssl, word32* left, int* ackOnly) +{ +#ifndef NO_ASN_TIME + int running; + word32 timeout; + word32 elapsed; +#ifdef WOLFSSL_DTLS13 + word32 ackLeft; +#endif + word32 ackStart = 0; + + if (!IsDtlsNotSctpMode(ssl) || ssl->dtls_timeout <= 0 || + ssl->buffers.outputBuffer.length > 0) + return 0; + running = DtlsTimersRunning(ssl, &ackStart); +#ifdef WOLFSSL_HAVE_HS_SUSPEND + /* A suspended handshake has not finished sending its flight. */ + if (running > 0 && IsHsSuspendErr(ssl->error)) + running &= ~DTLS_TIMER_RTX; +#endif + if (running <= 0) + return running; + + timeout = DtlsTimerTimeoutMs(ssl); + *left = timeout; + *ackOnly = 1; + if (running & DTLS_TIMER_RTX) { + elapsed = DtlsTimerElapsedMs(ssl->dtls_timer_start); + *left = (elapsed < timeout) ? timeout - elapsed : 0; +#ifdef WOLFSSL_DTLS13 + if (IsAtLeastTLSv1_3(ssl->version)) + *left = max(*left, Dtls13RtxWaitMs(ssl)); +#endif + *ackOnly = (*left > 0); + } +#ifdef WOLFSSL_DTLS13 + if (running & DTLS_TIMER_ACK) { + elapsed = DtlsTimerNowMs() - ackStart; + ackLeft = (elapsed < timeout / 4) ? timeout / 4 - elapsed : 0; + if (ackLeft < *left || !(running & DTLS_TIMER_RTX)) + *left = ackLeft; + } +#endif + return 1; +#else + (void)ssl; + (void)left; + (void)ackOnly; return 0; +#endif } -#ifndef NO_WOLFSSL_STUB -/* Handle a DTLS timeout. +/* Get the time left until the next DTLS timer expires. * - * Not implemented - stub for OpenSSL compatibility. + * @param [in] ssl SSL/TLS object. + * @param [out] timeleft Time left, zero once a timer expired. + * @return 1 when the timer runs. + * @return 0 when no timer runs, or ssl or timeleft is NULL. + */ +int wolfSSL_DTLSv1_get_timeout(WOLFSSL* ssl, WOLFSSL_TIMEVAL* timeleft) +{ + word32 left = 0; + int ackOnly = 0; + int ret; + + if (ssl == NULL || timeleft == NULL) + return 0; + + XMEMSET(timeleft, 0, sizeof(WOLFSSL_TIMEVAL)); + ret = DtlsTimerLeft(ssl, &left, &ackOnly); + if (ret < 0) + WOLFSSL_MSG("Getting the DTLS timers failed"); + if (ret <= 0) + return 0; + + timeleft->tv_sec = left / 1000; + timeleft->tv_usec = (left % 1000) * 1000; + return 1; +} + +/* Handle an expired DTLS timer. + * + * Retransmits the last flight, or for an expired DTLS 1.3 ack timer only + * sends the ACKs. * * @param [in] ssl SSL/TLS object. - * @return 0 always. + * @return 1 when a timer expired and was handled. + * @return 0 when no timer runs or it has not expired yet. + * @return WOLFSSL_FATAL_ERROR when ssl is NULL or retransmitting failed. */ int wolfSSL_DTLSv1_handle_timeout(WOLFSSL* ssl) { - WOLFSSL_STUB("SSL_DTLSv1_handle_timeout"); - (void)ssl; - return 0; + word32 left = 0; + int ackOnly = 0; + int ret; + + if (ssl == NULL) + return WOLFSSL_FATAL_ERROR; + + ret = DtlsTimerLeft(ssl, &left, &ackOnly); + if (ret < 0) { + ssl->error = ret; + WOLFSSL_ERROR(ret); + return WOLFSSL_FATAL_ERROR; + } + if (ret == 0 || left > 0) + return 0; + +#ifdef WOLFSSL_DTLS13 + if (IsAtLeastTLSv1_3(ssl->version)) { + ret = Dtls13GotTimeout(ssl, ackOnly ? DTLS_TIMER_ACK : DTLS_TIMER_RTX); + } + else +#endif + { + ret = wolfSSL_dtls_got_timeout(ssl); + } + if (ret != WOLFSSL_SUCCESS) + return WOLFSSL_FATAL_ERROR; + + return 1; } +#ifndef NO_WOLFSSL_STUB /* Set the initial DTLS timeout duration. * * Not implemented - stub for OpenSSL compatibility. @@ -1304,18 +1431,8 @@ int wolfSSL_dtls_got_timeout(WOLFSSL* ssl) return WOLFSSL_FATAL_ERROR; #ifdef WOLFSSL_DTLS13 - if (IsAtLeastTLSv1_3(ssl->version)) { - result = Dtls13RtxTimeout(ssl); - if (result < 0) { - if (result == WC_NO_ERR_TRACE(WANT_WRITE)) - ssl->dtls13SendingAckOrRtx = 1; - ssl->error = result; - WOLFSSL_ERROR(result); - return WOLFSSL_FATAL_ERROR; - } - - return WOLFSSL_SUCCESS; - } + if (IsAtLeastTLSv1_3(ssl->version)) + return Dtls13GotTimeout(ssl, 0); #endif /* WOLFSSL_DTLS13 */ /* Do we have any 1.2 messages stored? */ diff --git a/src/wolfio.c b/src/wolfio.c index 3b44df7a569..08de336b19c 100644 --- a/src/wolfio.c +++ b/src/wolfio.c @@ -738,23 +738,10 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx) /* Don't use ssl->options.handShakeDone since it is true even if * we are in the process of renegotiation */ - doDtlsTimeout = ssl->options.handShakeState != HANDSHAKE_DONE; - -#ifdef WOLFSSL_DTLS13 - if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) { - doDtlsTimeout = doDtlsTimeout || ssl->dtls13Rtx.rtxRecords != NULL; -#ifdef WOLFSSL_RW_THREADED - ret = wc_LockMutex(&ssl->dtls13Rtx.mutex); - if (ret != 0) - return ret; -#endif - doDtlsTimeout = doDtlsTimeout || - (ssl->dtls13FastTimeout && ssl->dtls13Rtx.seenRecords != NULL); -#ifdef WOLFSSL_RW_THREADED - wc_UnLockMutex(&ssl->dtls13Rtx.mutex); -#endif - } -#endif /* WOLFSSL_DTLS13 */ + ret = DtlsTimersRunning(ssl, NULL); + if (ret < 0) + return ret; + doDtlsTimeout = ssl->options.handShakeState != HANDSHAKE_DONE || ret != 0; do { int ignore = 0; diff --git a/tests/api/test_dtls.c b/tests/api/test_dtls.c index 2fb4f10ef28..2c486535618 100644 --- a/tests/api/test_dtls.c +++ b/tests/api/test_dtls.c @@ -2910,7 +2910,6 @@ int test_dtls_drop_client_ack(void) /* trigger client timeout, finished should be rtx */ ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); - ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); /* this should have triggered a rtx */ ExpectIntGT(test_ctx.s_msg_count, 0); @@ -3108,6 +3107,9 @@ int test_dtls_timeout(void) ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); ExpectIntEQ(wolfSSL_dtls_get_current_timeout(ssl_c), 1); #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DTLS13) + /* Don't wait DTLS13_MIN_RTX_INTERVAL since the last retransmission. */ + if (ssl_c != NULL) + ssl_c->dtls13Rtx.lastRtx = 0; /* will return 0 when not 1.3 */ if (wolfSSL_dtls13_use_quick_timeout(ssl_c)) ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); @@ -13790,8 +13792,8 @@ int test_wolfSSL_DTLSv1_compat_timeouts(void) ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl, &tv), 0); /* NULL arguments are tolerated. */ ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(NULL, NULL), 0); -#ifndef NO_WOLFSSL_STUB ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl), 0); +#ifndef NO_WOLFSSL_STUB wolfSSL_DTLSv1_set_initial_timeout_duration(ssl, 1000); #endif @@ -13801,6 +13803,885 @@ int test_wolfSSL_DTLSv1_compat_timeouts(void) return EXPECT_RESULT(); } +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_DTLS) && !defined(NO_ASN_TIME) +/* Move the flight's send time back past the current timeout. */ +static void test_DTLSv1_timer_expire(WOLFSSL* ssl) +{ + if (ssl != NULL) + ssl->dtls_timer_start -= (word32)(ssl->dtls_timeout + 1) * 1000; +} + +#ifdef WOLFSSL_DTLS13 +static void test_DTLSv1_ack_timer_expire(WOLFSSL* ssl) +{ + if (ssl != NULL) + ssl->dtls_ack_timer_start -= (word32)(ssl->dtls_timeout + 1) * 1000; +} +#endif + +static int test_DTLSv1_timer_left(WOLFSSL* ssl) +{ + WOLFSSL_TIMEVAL left; + + XMEMSET(&left, 0, sizeof(left)); + if (wolfSSL_DTLSv1_get_timeout(ssl, &left) != 1) + return -1; + return (int)(left.tv_sec * 1000 + left.tv_usec / 1000); +} + +static int test_DTLSv1_timer(method_provider method_c, + method_provider method_s) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_TIMEVAL left; + char buf[16]; + word32 now = 0; + int i; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_s, 1), WOLFSSL_SUCCESS); + + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(test_ctx.s_msg_count, 1); + XMEMSET(&left, 0, sizeof(left)); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 1); + ExpectTrue(left.tv_sec > 0 || left.tv_usec > 0); + ExpectTrue(left.tv_sec == 0 || (left.tv_sec == 1 && left.tv_usec == 0)); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + ExpectIntEQ(test_ctx.s_msg_count, 1); + + if (DtlsTimerLowRes()) { + /* A start one tick ago may be under a second, so 1 s hasn't passed. */ + for (i = 0; i < 3 && EXPECT_SUCCESS(); i++) { + now = DtlsTimerNowMs(); + ssl_c->dtls_timer_start = now - 1000; + XMEMSET(&left, 0, sizeof(left)); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 1); + if (DtlsTimerNowMs() == now) + break; + } + ExpectTrue(left.tv_sec > 0 || left.tv_usec > 0); + } + else { + if (ssl_c != NULL) + ssl_c->dtls_timer_start -= 300; + ExpectIntGT(test_DTLSv1_timer_left(ssl_c), 0); + ExpectIntLE(test_DTLSv1_timer_left(ssl_c), 700); + } + + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(test_DTLSv1_timer_left(ssl_c), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + ExpectIntEQ(test_ctx.s_msg_count, 2); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 2); + XMEMSET(&left, 0, sizeof(left)); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 1); + ExpectTrue(left.tv_sec >= 1 && left.tv_sec <= 2); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + ExpectIntEQ(test_ctx.s_msg_count, 2); + ExpectIntEQ(test_memio_drop_message(&test_ctx, 0, 1), 0); + + /* HelloVerifyRequest and HelloRetryRequest are not retransmitted. */ + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_s, &left), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntGT(test_DTLSv1_timer_left(ssl_s), 0); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + /* Let both sides consume any post-handshake messages and acks. */ + ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + if (wolfSSL_version(ssl_c) == DTLS1_3_VERSION) + TEST_DTLS13_PUMP(ssl_c); + ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + if (wolfSSL_version(ssl_s) == DTLS1_3_VERSION) + TEST_DTLS13_PUMP(ssl_s); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_s, &left), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_s), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* A zero timeout means no timeout, so no timer runs. */ +static int test_DTLSv1_timer_zero(method_provider method_c, + method_provider method_s) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_TIMEVAL left; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 0), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + ExpectIntEQ(test_ctx.s_msg_count, 1); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* At the maximum timeout the retransmission fails. */ +static int test_DTLSv1_timer_max(method_provider method_c, + method_provider method_s) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_dtls_set_timeout_max(ssl_c, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), WOLFSSL_FATAL_ERROR); + ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR), + WC_NO_ERR_TRACE(SOCKET_ERROR_E)); + ExpectIntEQ(test_ctx.s_msg_count, 1); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* Timeouts above DTLS_TIMEOUT_MAX are reported as DTLS_TIMEOUT_MAX. */ +static int test_DTLSv1_timer_large(method_provider method_c, + method_provider method_s) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_TIMEVAL left; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_max(ssl_c, 5000000), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 5000000), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + XMEMSET(&left, 0, sizeof(left)); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 1); + ExpectIntLE(left.tv_sec, DTLS_TIMEOUT_MAX); + ExpectIntGE(left.tv_sec, DTLS_TIMEOUT_MAX - 1); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +#ifndef WOLFSSL_NO_TLS12 +/* A valid handshake message resets the timeout and restarts the timer. */ +static int test_DTLSv1_timer_reset(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 2), WOLFSSL_SUCCESS); + + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + ExpectIntEQ(test_ctx.s_msg_count, 2); + ExpectIntEQ(test_memio_drop_message(&test_ctx, 0, 1), 0); + if (ssl_c != NULL) { + ExpectIntEQ(ssl_c->dtls_timeout, 4); + ssl_c->dtls_timer_start -= 3000; + } + ExpectIntGT(test_DTLSv1_timer_left(ssl_c), 0); + + /* Deliver only the ServerHello of the server's flight. */ + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntGT(test_ctx.c_msg_count, 1); + while (EXPECT_SUCCESS() && test_ctx.c_msg_count > 1) + ExpectIntEQ(test_memio_drop_message(&test_ctx, 1, 1), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 2); + ExpectIntGT(test_DTLSv1_timer_left(ssl_c), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + ExpectIntEQ(test_ctx.s_msg_count, 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} +#endif /* !WOLFSSL_NO_TLS12 */ + +#ifndef WOLFSSL_NO_TLS12 +/* A flight resent for a retransmitted peer flight restarts the timer. */ +static int test_DTLSv1_timer_peer_resend(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + word32 start = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntGT(test_ctx.c_msg_count, 0); + test_memio_clear_buffer(&test_ctx, 1); + + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + if (ssl_s != NULL) { + ssl_s->dtls_timer_start -= 2000; + start = ssl_s->dtls_timer_start; + } + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntGT(test_ctx.c_msg_count, 0); + if (ssl_s != NULL) + ExpectIntNE(ssl_s->dtls_timer_start, start); + ExpectIntGT(test_DTLSv1_timer_left(ssl_s), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} +#endif /* !WOLFSSL_NO_TLS12 */ + +/* No timer runs while our own write is blocked. */ +static int test_DTLSv1_timer_write_blocked(method_provider method_c, + method_provider method_s) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_TIMEVAL left; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s), 0); + + test_memio_simulate_want_write(&test_ctx, 1, 1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_WRITE); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 0); + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + test_memio_simulate_want_write(&test_ctx, 1, 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(test_ctx.s_msg_count, 1); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +#ifndef WOLFSSL_NO_TLS12 +/* No timer runs while a flight is still being written. */ +static int test_DTLSv1_timer_mid_flight(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_TIMEVAL left; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0); + + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + test_memio_simulate_want_write(&test_ctx, 1, 1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_WRITE); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 0); + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + test_memio_simulate_want_write(&test_ctx, 1, 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} +#endif /* !WOLFSSL_NO_TLS12 */ + +#if (!defined(WOLFSSL_NO_TLS12) && defined(HAVE_SECURE_RENEGOTIATION)) || \ + (defined(WOLFSSL_DTLS13) && defined(HAVE_SESSION_TICKET)) +/* A flight sent while an older one is kept gets a fresh timer. */ +static int test_DTLSv1_timer_new_flight(method_provider method_c, + method_provider method_s) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + int kept = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s), 0); +#ifdef HAVE_SECURE_RENEGOTIATION + ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_s), WOLFSSL_SUCCESS); +#endif + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectNotNull(ssl_s); + if (ssl_s != NULL) { + kept = ssl_s->dtls_tx_msg_list != NULL; +#ifdef WOLFSSL_DTLS13 + kept |= ssl_s->dtls13Rtx.rtxRecords != NULL; +#endif + } + ExpectIntEQ(kept, 1); + test_DTLSv1_timer_expire(ssl_s); + +#if defined(WOLFSSL_DTLS13) && defined(HAVE_SESSION_TICKET) + if (wolfSSL_version(ssl_s) == DTLS1_3_VERSION) + ExpectIntEQ(wolfSSL_update_keys(ssl_s), WOLFSSL_SUCCESS); +#endif +#if !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SECURE_RENEGOTIATION) + if (wolfSSL_version(ssl_s) != DTLS1_3_VERSION) { + ExpectIntEQ(wolfSSL_Rehandshake(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + } +#endif + ExpectIntGT(test_DTLSv1_timer_left(ssl_s), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_s), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} +#endif + +#ifdef WOLFSSL_DTLS13 +/* Records of a partly received flight are acked after a quarter timeout, + * without retransmitting or backing off. */ +static int test_DTLSv1_timer_ack(int blockAck) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + char held[TEST_MEMIO_BUF_SZ]; + int heldSz = (int)sizeof(held); + int left = 0; + int n = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 2), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_s, 2), WOLFSSL_SUCCESS); + + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + /* Hold back one record and drop the last one. */ + ExpectIntGE(n = test_ctx.c_msg_count, 3); + ExpectIntEQ(test_memio_copy_message(&test_ctx, 1, held, &heldSz, n - 2), + 0); + ExpectIntEQ(test_memio_drop_message(&test_ctx, 1, n - 1), 0); + ExpectIntEQ(test_memio_drop_message(&test_ctx, 1, n - 2), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(test_ctx.s_msg_count, 0); + ExpectIntGT(left = test_DTLSv1_timer_left(ssl_c), 0); + ExpectIntLE(left, 500); + + /* An ack timer that stopped with records left restarts with the next + * record. */ + if (ssl_c != NULL) + ssl_c->dtls13FastTimeout = 0; + test_DTLSv1_ack_timer_expire(ssl_c); + ExpectIntEQ(test_DTLSv1_timer_left(ssl_c), -1); + ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, held, heldSz), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntGT(left = test_DTLSv1_timer_left(ssl_c), 0); + ExpectIntLE(left, 500); + + test_DTLSv1_ack_timer_expire(ssl_c); + ExpectIntEQ(test_DTLSv1_timer_left(ssl_c), 0); + if (blockAck) { + /* A blocked ACK is reported and sent by the next call. */ + test_memio_simulate_want_write(&test_ctx, 1, 1); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), + WOLFSSL_FATAL_ERROR); + ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR), + WOLFSSL_ERROR_WANT_WRITE); + ExpectIntEQ(test_ctx.s_msg_count, 0); + test_memio_simulate_want_write(&test_ctx, 1, 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + } + else { + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + } + ExpectIntEQ(test_ctx.s_msg_count, 1); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 2); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + ExpectIntEQ(test_ctx.s_msg_count, 1); + test_memio_clear_buffer(&test_ctx, 0); + + /* The server resends what wasn't acked and the handshake completes. */ + test_DTLSv1_timer_expire(ssl_s); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_s), 1); + ExpectIntGT(test_ctx.c_msg_count, 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* A retransmission right after another one is held back without backing + * off. */ +static int test_DTLSv1_timer_throttle(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + test_DTLSv1_timer_expire(ssl_c); + /* A left over quick timeout flag doesn't hold back the retransmission. */ + if (ssl_c != NULL) + ssl_c->dtls13FastTimeout = 1; + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + ExpectIntEQ(test_ctx.s_msg_count, 2); + + test_DTLSv1_timer_expire(ssl_c); + ExpectIntGT(test_DTLSv1_timer_left(ssl_c), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(test_ctx.s_msg_count, 2); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 2); + + if (ssl_c != NULL) + ssl_c->dtls13Rtx.lastRtx -= 2000; + ExpectIntEQ(test_DTLSv1_timer_left(ssl_c), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + ExpectIntEQ(test_ctx.s_msg_count, 3); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 4); + +#ifndef WOLFSSL_32BIT_MILLI_TIME + /* A clock that stepped back doesn't hold the retransmission back. */ + test_DTLSv1_timer_expire(ssl_c); + if (ssl_c != NULL) + ssl_c->dtls13Rtx.lastRtx += 3600 * 1000; + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + ExpectIntEQ(test_ctx.s_msg_count, 4); +#endif + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* An ACK for the whole flight resets a backed off DTLS 1.3 timeout. */ +static int test_DTLSv1_timer_ack_reset(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + char buf[16]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_no_ticket_TLSv13(ssl_s), 0); + + /* ClientHello, HelloRetryRequest, ClientHello, server flight */ + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + /* Drop the server's ACK of the client Finished. */ + ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS); + test_memio_clear_buffer(&test_ctx, 1); + + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 2); + ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + TEST_DTLS13_PUMP(ssl_s); + ExpectIntEQ(wolfSSL_negotiate(ssl_c), WOLFSSL_SUCCESS); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 1); + ExpectIntEQ(test_DTLSv1_timer_left(ssl_c), -1); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* A flight resent for the peer's ACK restarts the timer. */ +static int test_DTLSv1_timer_ack_resend(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + word32 start = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 2), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_s, 2), WOLFSSL_SUCCESS); + + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntGE(test_ctx.c_msg_count, 2); + ExpectIntEQ(test_memio_drop_message(&test_ctx, 1, + test_ctx.c_msg_count - 1), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + test_DTLSv1_ack_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + ExpectIntEQ(test_ctx.s_msg_count, 1); + + if (ssl_s != NULL) { + ssl_s->dtls_timer_start -= 1500; + start = ssl_s->dtls_timer_start; + } + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntGT(test_ctx.c_msg_count, 0); + if (ssl_s != NULL) + ExpectIntNE(ssl_s->dtls_timer_start, start); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* A new flight drops the quick timeout of the records it acks, so the next + * timeout retransmits it. */ +static int test_DTLSv1_timer_flight_acks(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + int sent = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + /* The client takes the whole server flight and sends its Finished. */ + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntGT(sent = test_ctx.s_msg_count, 0); + ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); + ExpectIntGT(test_ctx.s_msg_count, sent); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* With the quick timeout flag set, got_timeout only retransmits and backs + * off once the flight waited half its timeout. */ +static int test_DTLSv1_timer_got_timeout_quick(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(test_ctx.s_msg_count, 1); + + if (ssl_c != NULL) + ssl_c->dtls13FastTimeout = 1; + ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(test_ctx.s_msg_count, 1); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 1); + + if (ssl_c != NULL) + ssl_c->dtls13FastTimeout = 1; + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(test_ctx.s_msg_count, 2); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 2); + + /* With no timeout a quick timeout only sends the ACKs. */ + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 0), WOLFSSL_SUCCESS); + if (ssl_c != NULL) { + ssl_c->dtls13FastTimeout = 1; + ssl_c->dtls13Rtx.lastRtx = 0; + } + ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(test_ctx.s_msg_count, 2); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +#if !defined(NO_RSA) && !defined(NO_FILESYSTEM) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) +/* A timeout with records waiting for an ack and no flight to resend only + * sends the ACK. */ +static int test_DTLSv1_timer_got_timeout_acks(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL), + WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + /* The server only gets the first record of the client's flight. */ + ExpectIntGE(test_ctx.s_msg_count, 2); + while (EXPECT_SUCCESS() && test_ctx.s_msg_count > 1) + ExpectIntEQ(test_memio_drop_message(&test_ctx, 0, 1), 0); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(test_ctx.c_msg_count, 0); + ExpectIntEQ(wolfSSL_dtls13_use_quick_timeout(ssl_s), 1); + + ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_s), WOLFSSL_SUCCESS); + ExpectIntEQ(test_ctx.c_msg_count, 1); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->dtls_timeout, 1); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} +#endif + +#ifdef WOLFSSL_ASYNC_CRYPT_SW +/* No timer runs while crypto suspends the server part way through a flight. */ +static int test_DTLSv1_timer_suspended(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_TIMEVAL left; + int devId = INVALID_DEVID; + int suspended = 0; + int i; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(wolfAsync_DevOpen(&devId), 0); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_SetDevId(ssl_s, devId), WOLFSSL_SUCCESS); + + for (i = 0; i < 50 && EXPECT_SUCCESS() && !suspended; i++) { + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + if (wolfSSL_get_error(ssl_s, -1) == WC_NO_ERR_TRACE(WC_PENDING_E)) { + suspended = ssl_s->dtls13Rtx.rtxRecords != NULL; + if (!suspended) { + ExpectIntGE(wolfSSL_AsyncPoll(ssl_s, WOLF_POLL_FLAG_CHECK_HW), + 0); + } + } + } + ExpectIntEQ(suspended, 1); + test_DTLSv1_timer_expire(ssl_s); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_s, &left), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_s), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 50, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + wolfAsync_DevClose(&devId); + return EXPECT_RESULT(); +} +#endif +#endif /* WOLFSSL_DTLS13 */ +#endif + +int test_wolfSSL_DTLSv1_timer(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_DTLS) && !defined(NO_ASN_TIME) +#ifndef WOLFSSL_NO_TLS12 + ExpectIntEQ(test_DTLSv1_timer(wolfDTLSv1_2_client_method, + wolfDTLSv1_2_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_zero(wolfDTLSv1_2_client_method, + wolfDTLSv1_2_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_max(wolfDTLSv1_2_client_method, + wolfDTLSv1_2_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_large(wolfDTLSv1_2_client_method, + wolfDTLSv1_2_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_reset(), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_peer_resend(), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_mid_flight(), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_write_blocked(wolfDTLSv1_2_client_method, + wolfDTLSv1_2_server_method), TEST_SUCCESS); +#ifdef HAVE_SECURE_RENEGOTIATION + ExpectIntEQ(test_DTLSv1_timer_new_flight(wolfDTLSv1_2_client_method, + wolfDTLSv1_2_server_method), TEST_SUCCESS); +#endif +#endif +#ifdef WOLFSSL_DTLS13 + ExpectIntEQ(test_DTLSv1_timer(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_zero(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_max(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_large(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_write_blocked(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method), TEST_SUCCESS); +#ifdef HAVE_SESSION_TICKET + ExpectIntEQ(test_DTLSv1_timer_new_flight(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method), TEST_SUCCESS); +#endif + ExpectIntEQ(test_DTLSv1_timer_ack(0), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_ack(1), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_throttle(), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_ack_resend(), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_ack_reset(), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_flight_acks(), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_got_timeout_quick(), TEST_SUCCESS); +#if !defined(NO_RSA) && !defined(NO_FILESYSTEM) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) + ExpectIntEQ(test_DTLSv1_timer_got_timeout_acks(), TEST_SUCCESS); +#endif +#ifdef WOLFSSL_ASYNC_CRYPT_SW + ExpectIntEQ(test_DTLSv1_timer_suspended(), TEST_SUCCESS); +#endif +#endif +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_dtls13_set_send_more_acks(void) { EXPECT_DECLS; diff --git a/tests/api/test_dtls.h b/tests/api/test_dtls.h index 2a96a9bb731..5f0d6d51e9d 100644 --- a/tests/api/test_dtls.h +++ b/tests/api/test_dtls.h @@ -112,6 +112,7 @@ int test_wolfSSL_dtls_get0_peer(void); int test_wolfSSL_dtls_set_timeout_init(void); int test_wolfSSL_dtls_retransmit(void); int test_wolfSSL_DTLSv1_compat_timeouts(void); +int test_wolfSSL_DTLSv1_timer(void); int test_wolfSSL_dtls13_set_send_more_acks(void); int test_wolfSSL_dtls_srtp_keying_material(void); int test_wolfSSL_mcast_peers(void); @@ -357,6 +358,7 @@ int test_dtls_no_cookie_ch_pause(WOLFSSL* ssl, void* ctx); TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_set_timeout_init), \ TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_retransmit), \ TEST_DECL_GROUP("dtls", test_wolfSSL_DTLSv1_compat_timeouts), \ + TEST_DECL_GROUP("dtls", test_wolfSSL_DTLSv1_timer), \ TEST_DECL_GROUP("dtls", test_wolfSSL_dtls13_set_send_more_acks), \ TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_srtp_keying_material), \ TEST_DECL_GROUP("dtls", test_wolfSSL_mcast_peers), \ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 3974ba87cbc..05c596cfe35 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -7234,6 +7234,10 @@ struct WOLFSSL { int dtls_timeout; /* current timeout value, changes */ #ifndef NO_ASN_TIME word32 dtls_start_timeout; + word32 dtls_timer_start; /* ms, when the flight was sent */ +#ifdef WOLFSSL_DTLS13 + word32 dtls_ack_timer_start; /* ms, first record to ack */ +#endif #endif /* !NO_ASN_TIME */ word32 dtls_tx_msg_list_sz; word32 dtls_rx_msg_list_sz; @@ -8102,6 +8106,8 @@ WOLFSSL_LOCAL int Tls13KeyUpdateLimitReached(WOLFSSL* ssl); #define DtlsMsgListNextReady wolfSSL_DtlsMsgListNextReady #define DtlsMsgFind wolfSSL_DtlsMsgFind #define DtlsMsgStore wolfSSL_DtlsMsgStore + #define DtlsTimerNowMs wolfSSL_DtlsTimerNowMs + #define DtlsTimerLowRes wolfSSL_DtlsTimerLowRes #endif /* WOLFSSL_API_PREFIX_MAP */ WOLFSSL_LOCAL DtlsMsg* DtlsMsgNew(word32 sz, byte tx, void* heap); WOLFSSL_LOCAL void DtlsMsgDelete(DtlsMsg* item, void* heap); @@ -8122,9 +8128,23 @@ WOLFSSL_LOCAL int Tls13KeyUpdateLimitReached(WOLFSSL* ssl); void* heap); WOLFSSL_LOCAL DtlsMsg* DtlsMsgInsert(DtlsMsg* head, DtlsMsg* item); + /* DTLS timers returned by DtlsTimersRunning(). */ + enum DtlsTimer { + DTLS_TIMER_RTX = 0x1, /* a flight waits for a reply */ + DTLS_TIMER_ACK = 0x2 /* DTLS 1.3 records wait for an ack */ + }; WOLFSSL_LOCAL int DtlsMsgPoolSave(WOLFSSL* ssl, const byte* data, word32 dataSz, enum HandShakeType type); WOLFSSL_LOCAL int DtlsMsgPoolTimeout(WOLFSSL* ssl); + WOLFSSL_LOCAL void DtlsTimerStart(WOLFSSL* ssl); + WOLFSSL_LOCAL void DtlsTimeoutReset(WOLFSSL* ssl); + WOLFSSL_LOCAL int DtlsTimersRunning(WOLFSSL* ssl, word32* ackStart); +#ifndef NO_ASN_TIME + WOLFSSL_TEST_VIS word32 DtlsTimerNowMs(void); + WOLFSSL_TEST_VIS int DtlsTimerLowRes(void); + WOLFSSL_LOCAL word32 DtlsTimerElapsedMs(word32 start); + WOLFSSL_LOCAL word32 DtlsTimerTimeoutMs(const WOLFSSL* ssl); +#endif WOLFSSL_LOCAL int VerifyForDtlsMsgPoolSend(WOLFSSL* ssl, byte type, word32 fragOffset); WOLFSSL_LOCAL int VerifyForTxDtlsMsgDelete(WOLFSSL* ssl, DtlsMsg* item); @@ -8170,8 +8190,8 @@ WOLFSSL_LOCAL int Tls13KeyUpdateLimitReached(WOLFSSL* ssl); WOLFSSL_LOCAL void WriteSEQ(WOLFSSL* ssl, int verifyOrder, byte* out); -#if defined(WOLFSSL_TLS13) && (defined(HAVE_SESSION_TICKET) || \ - !defined(NO_PSK) || defined(WOLFSSL_DTLS13)) +#if (defined(WOLFSSL_TLS13) && (defined(HAVE_SESSION_TICKET) || \ + !defined(NO_PSK))) || defined(WOLFSSL_DTLS) #ifdef WOLFSSL_32BIT_MILLI_TIME WOLFSSL_LOCAL word32 TimeNowInMilliseconds(void); #else @@ -8502,7 +8522,9 @@ WOLFSSL_LOCAL int Dtls13HashClientHello(const WOLFSSL* ssl, byte* hash, WOLFSSL_LOCAL void Dtls13FreeFsmResources(WOLFSSL* ssl); WOLFSSL_LOCAL void Dtls13RtxFlushBuffered(WOLFSSL* ssl, byte keepNewSessionTicket); -WOLFSSL_LOCAL int Dtls13RtxTimeout(WOLFSSL* ssl); +WOLFSSL_LOCAL int Dtls13RtxTimeout(WOLFSSL* ssl, int expired); +WOLFSSL_LOCAL word32 Dtls13RtxWaitMs(WOLFSSL* ssl); +WOLFSSL_LOCAL int Dtls13AckSendable(const WOLFSSL* ssl); WOLFSSL_LOCAL int Dtls13ProcessBufferedMessages(WOLFSSL* ssl); WOLFSSL_LOCAL int Dtls13CheckAEADFailLimit(WOLFSSL* ssl); WOLFSSL_LOCAL int Dtls13UpdateWindowRecordRecvd(WOLFSSL* ssl);