Skip to content

Commit 45fa35f

Browse files
committed
Fix for DTLS1.3 HRR group handling
When a server uses a HRR to negotiate the key exchange group to use, the selected group is advertised in the HRR key share extension. Furthermore, this group is also stored in the Cookie that is sent to the client. When the server receives the second CH, the group used in the key share extension MUST be the one of the HRR. For stateless DTLS servers, the handling of this check had a bug. The key share group of the HRR is stored in the ssl->hrr_keyshare_group variable and is checked against the received key share of the second CH. However, in the stateless server case, another CH message may be received inbetween the two CH message of the desired client, potentially overwriting the ssl->hrr_keyshare_group variable. This then causes handshake failures when the ssl->hrr_keyshare_group variable contains another group than the second CH message of the desired client. To fix this, two changes are conducted: 1. Disable the ssl->hrr_keyshare_group check for stateless DTLS 1.3 servers. As long as the server is stateless, CHs from multiple clients may be received that individually cause HRRs with different groups. For each of these clients, the HRR group is properly stored in the cookie. 2. When a valid cookie is received from the client, the server becomes stateful. In this case, we now parse the cookie for a stored HRR group. If present, we reset the ssl->hrr_keyshare_group variable to this group to ensure the error checks succeed. A new test is added to check for this behavior.
1 parent 215fe13 commit 45fa35f

4 files changed

Lines changed: 169 additions & 1 deletion

File tree

src/dtls.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,42 @@ static int CheckDtlsCookie(const WOLFSSL* ssl, WolfSSL_CH* ch,
295295
return ret;
296296
}
297297

298+
#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_SEND_HRR_COOKIE)
299+
static int ParseCookieExt(WOLFSSL* ssl, WolfSSL_CH* ch)
300+
{
301+
int ret = 0;
302+
word16 len;
303+
const byte* cookie = NULL;
304+
305+
if (ch->cookieExt.size < OPAQUE16_LEN + 1)
306+
return BUFFER_E;
307+
ato16(ch->cookieExt.elements, &len);
308+
if (ch->cookieExt.size - OPAQUE16_LEN != len)
309+
return BUFFER_E;
310+
cookie = ch->cookieExt.elements + OPAQUE16_LEN;
311+
ret = TlsCheckCookie(ssl, cookie, len);
312+
if (ret < 0)
313+
return ret;
314+
len = (word16)ret;
315+
316+
/* Cookie Data = Hash Len | Hash | CS | KeyShare Group (optional) */
317+
318+
/* Check if the cookie has a key share group */
319+
if (len > cookie[0] + 4) {
320+
word16 keyShareGroup = 0;
321+
ato16(cookie + 3 + cookie[0], &keyShareGroup);
322+
323+
/* The key share group in the cookie is the group selected by the
324+
* server in the HelloRetryRequest. Hence, the client must use this
325+
* group in the second ClientHello.
326+
*/
327+
ssl->hrr_keyshare_group = keyShareGroup;
328+
}
329+
330+
return 0;
331+
}
332+
#endif /* WOLFSSL_DTLS13 && WOLFSSL_SEND_HRR_COOKIE */
333+
298334
static int ParseClientHello(const byte* input, word32 helloSz, WolfSSL_CH* ch,
299335
byte isFirstCHFrag)
300336
{
@@ -1041,6 +1077,10 @@ int DoClientHelloStateless(WOLFSSL* ssl, const byte* input, word32 helloSz,
10411077
e = Dtls13GetEpoch(ssl, ssl->keys.curEpoch64);
10421078
if (e != NULL)
10431079
XMEMSET(e->window, 0xFF, sizeof(e->window));
1080+
1081+
ret = ParseCookieExt(ssl, &ch);
1082+
if (ret != 0)
1083+
return ret;
10441084
}
10451085
else
10461086
#endif

src/tls.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10217,6 +10217,18 @@ int TLSX_KeyShare_Parse_ClientHello(const WOLFSSL* ssl,
1021710217
}
1021810218

1021910219
if (ssl->hrr_keyshare_group != 0) {
10220+
#ifdef WOLFSSL_DTLS
10221+
/* In case of stateless DTLSv1.3, the ssl->hrr_keyshare_group variable
10222+
* may have already been set without further proceeding the handshake,
10223+
* so we never received the second ClientHello with the selected group.
10224+
* Hence, when we receive another ClientHello message, we do not
10225+
* consider that as an error here.
10226+
*/
10227+
if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version) &&
10228+
!ssl->options.dtlsStateful) {
10229+
return 0;
10230+
}
10231+
#endif
1022010232
/*
1022110233
* https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.8
1022210234
* when sending the new ClientHello, the client MUST

tests/api/test_dtls.c

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2567,3 +2567,117 @@ int test_dtls13_min_rtx_interval(void)
25672567
#endif
25682568
return EXPECT_RESULT();
25692569
}
2570+
2571+
/* DTLS stateless API handling multiple CHs with different HRR groups */
2572+
int test_dtls_stateless_hrr_group(void)
2573+
{
2574+
EXPECT_DECLS;
2575+
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS)
2576+
size_t i;
2577+
struct {
2578+
method_provider client_meth;
2579+
method_provider server_meth;
2580+
} params[] = {
2581+
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DTLS13)
2582+
{ wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method },
2583+
#endif
2584+
#if !defined(WOLFSSL_NO_TLS12) && defined(WOLFSSL_DTLS)
2585+
{ wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method },
2586+
#endif
2587+
};
2588+
XMEMSET(&test_memio_wolfio_ctx, 0, sizeof(test_memio_wolfio_ctx));
2589+
for (i = 0; i < XELEM_CNT(params) && !EXPECT_FAIL(); i++) {
2590+
WOLFSSL_CTX *ctx_s = NULL, *ctx_c = NULL;
2591+
WOLFSSL *ssl_s = NULL, *ssl_c1 = NULL, *ssl_c2 = NULL;
2592+
struct test_memio_ctx test_ctx;
2593+
int groups_1[] = {
2594+
WOLFSSL_ECC_SECP256R1,
2595+
WOLFSSL_ECC_SECP384R1,
2596+
WOLFSSL_ECC_SECP521R1
2597+
};
2598+
int groups_2[] = {
2599+
WOLFSSL_ECC_SECP384R1,
2600+
WOLFSSL_ECC_SECP521R1
2601+
};
2602+
char hrrBuf[1000];
2603+
int hrrSz = sizeof(hrrBuf);
2604+
2605+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
2606+
2607+
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c1, &ssl_s,
2608+
params[i].client_meth, params[i].server_meth), 0);
2609+
2610+
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, NULL, &ssl_c2, NULL,
2611+
params[i].client_meth, params[i].server_meth), 0);
2612+
2613+
test_memio_wolfio_ctx.test_ctx = &test_ctx;
2614+
test_memio_wolfio_ctx.ssl_s = ssl_s;
2615+
/* Large number to error out if any syscalls are called with it */
2616+
test_memio_wolfio_ctx.fd = 6000;
2617+
XMEMSET(&test_memio_wolfio_ctx.peer_addr, 0,
2618+
sizeof(test_memio_wolfio_ctx.peer_addr));
2619+
test_memio_wolfio_ctx.peer_addr.ss_family = AF_INET;
2620+
2621+
wolfSSL_SetLoggingPrefix("server");
2622+
wolfSSL_dtls_set_using_nonblock(ssl_s, 1);
2623+
wolfSSL_SetRecvFrom(ssl_s, test_memio_wolfio_recvfrom);
2624+
/* Restore default functions */
2625+
wolfSSL_SSLSetIORecv(ssl_s, EmbedReceiveFrom);
2626+
ExpectIntEQ(wolfSSL_set_read_fd(ssl_s, test_memio_wolfio_ctx.fd),
2627+
WOLFSSL_SUCCESS);
2628+
2629+
/* Set groups and disable key shares. This ensures that only the given
2630+
* groups are in the SupportedGroups extension and that an empty key
2631+
* share extension is sent in the initial ClientHello of each session.
2632+
* This triggers the server to send a HelloRetryRequest with the first
2633+
* group in the SupportedGroups extension selected. */
2634+
wolfSSL_SetLoggingPrefix("client1");
2635+
ExpectIntEQ(wolfSSL_set_groups(ssl_c1, groups_1, 3), WOLFSSL_SUCCESS);
2636+
ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c1), WOLFSSL_SUCCESS);
2637+
2638+
wolfSSL_SetLoggingPrefix("client2");
2639+
ExpectIntEQ(wolfSSL_set_groups(ssl_c2, groups_2, 2), WOLFSSL_SUCCESS);
2640+
ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c2), WOLFSSL_SUCCESS);
2641+
2642+
/* Start handshake, send first ClientHello */
2643+
wolfSSL_SetLoggingPrefix("client1");
2644+
ExpectIntEQ(wolfSSL_connect(ssl_c1), -1);
2645+
ExpectIntEQ(wolfSSL_get_error(ssl_c1, -1), WOLFSSL_ERROR_WANT_READ);
2646+
2647+
/* Read first ClientHello, send HRR with WOLFSSL_ECC_SECP256R1 */
2648+
wolfSSL_SetLoggingPrefix("server");
2649+
ExpectIntEQ(wolfDTLS_accept_stateless(ssl_s), 0);
2650+
ExpectIntEQ(test_memio_copy_message(&test_ctx, 1, hrrBuf, &hrrSz, 0), 0);
2651+
ExpectIntGT(hrrSz, 0);
2652+
test_memio_clear_buffer(&test_ctx, 1);
2653+
2654+
/* Send second ClientHello */
2655+
wolfSSL_SetLoggingPrefix("client2");
2656+
ExpectIntEQ(wolfSSL_connect(ssl_c2), -1);
2657+
ExpectIntEQ(wolfSSL_get_error(ssl_c2, -1), WOLFSSL_ERROR_WANT_READ);
2658+
2659+
/* Read second ClientHello, send HRR now with WOLFSSL_ECC_SECP384R1 */
2660+
wolfSSL_SetLoggingPrefix("server");
2661+
ExpectIntEQ(wolfDTLS_accept_stateless(ssl_s), 0);
2662+
test_memio_clear_buffer(&test_ctx, 1);
2663+
2664+
/* Complete first handshake with WOLFSSL_ECC_SECP256R1 */
2665+
wolfSSL_SetLoggingPrefix("client1");
2666+
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, hrrBuf, hrrSz), 0);
2667+
ExpectIntEQ(wolfSSL_connect(ssl_c1), -1);
2668+
ExpectIntEQ(wolfSSL_get_error(ssl_c1, -1), WOLFSSL_ERROR_WANT_READ);
2669+
2670+
wolfSSL_SetLoggingPrefix("server");
2671+
ExpectIntEQ(wolfDTLS_accept_stateless(ssl_s), WOLFSSL_SUCCESS);
2672+
2673+
ExpectIntEQ(test_memio_do_handshake(ssl_c1, ssl_s, 10, NULL), 0);
2674+
2675+
wolfSSL_free(ssl_s);
2676+
wolfSSL_free(ssl_c1);
2677+
wolfSSL_free(ssl_c2);
2678+
wolfSSL_CTX_free(ctx_s);
2679+
wolfSSL_CTX_free(ctx_c);
2680+
}
2681+
#endif
2682+
return EXPECT_RESULT();
2683+
}

tests/api/test_dtls.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ int test_dtls_memio_wolfio_stateless(void);
5050
int test_dtls_mtu_fragment_headroom(void);
5151
int test_dtls_mtu_split_messages(void);
5252
int test_dtls13_min_rtx_interval(void);
53+
int test_dtls_stateless_hrr_group(void);
5354

5455
#define TEST_DTLS_DECLS \
5556
TEST_DECL_GROUP("dtls", test_dtls12_basic_connection_id), \
@@ -79,5 +80,6 @@ int test_dtls13_min_rtx_interval(void);
7980
TEST_DECL_GROUP("dtls", test_dtls_mtu_fragment_headroom), \
8081
TEST_DECL_GROUP("dtls", test_dtls_mtu_split_messages), \
8182
TEST_DECL_GROUP("dtls", test_dtls_memio_wolfio_stateless), \
82-
TEST_DECL_GROUP("dtls", test_dtls13_min_rtx_interval)
83+
TEST_DECL_GROUP("dtls", test_dtls13_min_rtx_interval), \
84+
TEST_DECL_GROUP("dtls", test_dtls_stateless_hrr_group)
8385
#endif /* TESTS_API_DTLS_H */

0 commit comments

Comments
 (0)