Skip to content

Commit a1d7c8d

Browse files
committed
DTLS1.3: Implement RFC 9147 legacy_session_id_echo requirements
1 parent 2059d64 commit a1d7c8d

4 files changed

Lines changed: 153 additions & 19 deletions

File tree

src/dtls.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -857,9 +857,9 @@ static int SendStatelessReplyDtls13(const WOLFSSL* ssl, WolfSSL_CH* ch)
857857
nonConstSSL->options.tls1_1 = 1;
858858
nonConstSSL->options.tls1_3 = 1;
859859

860-
XMEMCPY(nonConstSSL->session->sessionID, ch->sessionId.elements,
861-
ch->sessionId.size);
862-
nonConstSSL->session->sessionIDSz = (byte)ch->sessionId.size;
860+
/* RFC 9147 Section 5.3: DTLS 1.3 ServerHello must have empty
861+
* legacy_session_id_echo. Don't copy the client's session ID. */
862+
nonConstSSL->session->sessionIDSz = 0;
863863
nonConstSSL->options.cipherSuite0 = cs.cipherSuite0;
864864
nonConstSSL->options.cipherSuite = cs.cipherSuite;
865865
nonConstSSL->extensions = parsedExts;

src/tls13.c

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4643,6 +4643,13 @@ int SendTls13ClientHello(WOLFSSL* ssl)
46434643
ssl->session->sessionIDSz = 0;
46444644
ssl->options.tls13MiddleBoxCompat = 0;
46454645
}
4646+
#endif
4647+
#ifdef WOLFSSL_DTLS13
4648+
if (ssl->options.dtls) {
4649+
/* RFC 9147 Section 5: DTLS implementations do not use the
4650+
* TLS 1.3 "compatibility mode" */
4651+
ssl->options.tls13MiddleBoxCompat = 0;
4652+
}
46464653
#endif
46474654
GetTls13SessionId(ssl, NULL, &sessIdSz);
46484655
args->length += (word16)sessIdSz;
@@ -5586,16 +5593,25 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
55865593
}
55875594
else
55885595
#endif /* WOLFSSL_TLS13_MIDDLEBOX_COMPAT */
5596+
#if defined(WOLFSSL_QUIC) || defined(WOLFSSL_DTLS13)
5597+
if (0
55895598
#ifdef WOLFSSL_QUIC
5590-
if (WOLFSSL_IS_QUIC(ssl)) {
5599+
|| WOLFSSL_IS_QUIC(ssl)
5600+
#endif
5601+
#ifdef WOLFSSL_DTLS13
5602+
|| ssl->options.dtls
5603+
#endif
5604+
) {
5605+
/* RFC 9147 Section 5.3 / RFC 9001 Section 8.4: DTLS 1.3 and QUIC
5606+
* ServerHello must have empty legacy_session_id_echo. */
55915607
if (args->sessIdSz != 0) {
55925608
WOLFSSL_MSG("args->sessIdSz != 0");
55935609
WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
55945610
return INVALID_PARAMETER;
55955611
}
55965612
}
55975613
else
5598-
#endif /* WOLFSSL_QUIC */
5614+
#endif /* WOLFSSL_QUIC || WOLFSSL_DTLS13 */
55995615
if (args->sessIdSz != ssl->session->sessionIDSz || (args->sessIdSz > 0 &&
56005616
XMEMCMP(ssl->session->sessionID, args->sessId, args->sessIdSz) != 0))
56015617
{
@@ -6558,6 +6574,7 @@ static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie)
65586574
word16 length;
65596575
int keyShareExt = 0;
65606576
int ret;
6577+
byte sessIdSz;
65616578

65626579
ret = TlsCheckCookie(ssl, cookie->data, (byte)cookie->len);
65636580
if (ret < 0)
@@ -6582,7 +6599,13 @@ static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie)
65826599
return ret;
65836600

65846601
/* Reconstruct the HelloRetryMessage for handshake hash. */
6585-
length = HRR_BODY_SZ - ID_LEN + ssl->session->sessionIDSz +
6602+
sessIdSz = ssl->session->sessionIDSz;
6603+
#ifdef WOLFSSL_DTLS13
6604+
/* RFC 9147 Section 5.3: DTLS 1.3 must use empty legacy_session_id. */
6605+
if (ssl->options.dtls)
6606+
sessIdSz = 0;
6607+
#endif
6608+
length = HRR_BODY_SZ - ID_LEN + sessIdSz +
65866609
HRR_COOKIE_HDR_SZ + cookie->len;
65876610
length += HRR_VERSIONS_SZ;
65886611
/* HashSz (1 byte) + Hash (HashSz bytes) + CipherSuite (2 bytes) */
@@ -6609,10 +6632,10 @@ static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie)
66096632
XMEMCPY(hrr + hrrIdx, helloRetryRequestRandom, RAN_LEN);
66106633
hrrIdx += RAN_LEN;
66116634

6612-
hrr[hrrIdx++] = ssl->session->sessionIDSz;
6613-
if (ssl->session->sessionIDSz > 0) {
6614-
XMEMCPY(hrr + hrrIdx, ssl->session->sessionID, ssl->session->sessionIDSz);
6615-
hrrIdx += ssl->session->sessionIDSz;
6635+
hrr[hrrIdx++] = sessIdSz;
6636+
if (sessIdSz > 0) {
6637+
XMEMCPY(hrr + hrrIdx, ssl->session->sessionID, sessIdSz);
6638+
hrrIdx += sessIdSz;
66166639
}
66176640

66186641
/* Restore the cipher suite from the cookie. */
@@ -6625,7 +6648,7 @@ static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie)
66256648
hrr[hrrIdx++] = 0;
66266649

66276650
/* Extensions' length */
6628-
length -= HRR_BODY_SZ - ID_LEN + ssl->session->sessionIDSz;
6651+
length -= HRR_BODY_SZ - ID_LEN + sessIdSz;
66296652
c16toa(length, hrr + hrrIdx);
66306653
hrrIdx += 2;
66316654

@@ -7050,9 +7073,20 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
70507073
if (sessIdSz + args->idx > helloSz)
70517074
ERROR_OUT(BUFFER_ERROR, exit_dch);
70527075

7053-
ssl->session->sessionIDSz = sessIdSz;
7054-
if (sessIdSz > 0)
7055-
XMEMCPY(ssl->session->sessionID, input + args->idx, sessIdSz);
7076+
#ifdef WOLFSSL_DTLS13
7077+
/* RFC 9147 Section 5.3: DTLS 1.3 ServerHello must have empty
7078+
* legacy_session_id_echo. Don't store the client's value so it
7079+
* won't be echoed in SendTls13ServerHello. */
7080+
if (ssl->options.dtls) {
7081+
ssl->session->sessionIDSz = 0;
7082+
}
7083+
else
7084+
#endif
7085+
{
7086+
ssl->session->sessionIDSz = sessIdSz;
7087+
if (sessIdSz > 0)
7088+
XMEMCPY(ssl->session->sessionID, input + args->idx, sessIdSz);
7089+
}
70567090
args->idx += sessIdSz;
70577091

70587092
#ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
@@ -7625,10 +7659,21 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType)
76257659
WOLFSSL_BUFFER(ssl->arrays->serverRandom, RAN_LEN);
76267660
#endif
76277661

7628-
output[idx++] = ssl->session->sessionIDSz;
7629-
if (ssl->session->sessionIDSz > 0) {
7630-
XMEMCPY(output + idx, ssl->session->sessionID, ssl->session->sessionIDSz);
7631-
idx += ssl->session->sessionIDSz;
7662+
#ifdef WOLFSSL_DTLS13
7663+
if (ssl->options.dtls) {
7664+
/* RFC 9147 Section 5.3: DTLS 1.3 ServerHello must have empty
7665+
* legacy_session_id_echo. */
7666+
output[idx++] = 0;
7667+
}
7668+
else
7669+
#endif
7670+
{
7671+
output[idx++] = ssl->session->sessionIDSz;
7672+
if (ssl->session->sessionIDSz > 0) {
7673+
XMEMCPY(output + idx, ssl->session->sessionID,
7674+
ssl->session->sessionIDSz);
7675+
idx += ssl->session->sessionIDSz;
7676+
}
76327677
}
76337678

76347679
/* Chosen cipher suite */

tests/api/test_dtls.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2609,3 +2609,90 @@ int test_dtls13_min_rtx_interval(void)
26092609
#endif
26102610
return EXPECT_RESULT();
26112611
}
2612+
2613+
/* RFC 9147 Section 5.3: DTLS 1.3 ServerHello must have empty
2614+
* legacy_session_id_echo, even if the ClientHello had a non-empty
2615+
* legacy_session_id. */
2616+
int test_dtls13_no_session_id_echo(void)
2617+
{
2618+
EXPECT_DECLS;
2619+
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) && \
2620+
defined(HAVE_SESSION_TICKET)
2621+
struct test_memio_ctx test_ctx;
2622+
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
2623+
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
2624+
WOLFSSL_SESSION *sess = NULL;
2625+
char readBuf[1];
2626+
/* Use traditional groups to avoid HRR from PQ key share mismatch */
2627+
int groups[] = {
2628+
WOLFSSL_ECC_SECP256R1,
2629+
WOLFSSL_ECC_SECP384R1,
2630+
};
2631+
2632+
/* First connection: complete a DTLS 1.3 handshake to get a session */
2633+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
2634+
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
2635+
wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
2636+
ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 2), WOLFSSL_SUCCESS);
2637+
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
2638+
2639+
/* Read to process any NewSessionTicket */
2640+
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
2641+
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
2642+
2643+
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
2644+
2645+
/* Ensure the session has a non-empty session ID so the ClientHello
2646+
* will have a populated legacy_session_id field (which is legal per
2647+
* RFC 9147). */
2648+
if (sess->sessionIDSz == 0) {
2649+
sess->sessionIDSz = ID_LEN;
2650+
XMEMSET(sess->sessionID, 0x42, ID_LEN);
2651+
}
2652+
2653+
wolfSSL_free(ssl_c); ssl_c = NULL;
2654+
wolfSSL_free(ssl_s); ssl_s = NULL;
2655+
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
2656+
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
2657+
2658+
/* Second connection: set the session on the client so the ClientHello
2659+
* contains a non-empty legacy_session_id. Verify the server does NOT
2660+
* echo it in the ServerHello. */
2661+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
2662+
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
2663+
wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
2664+
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
2665+
/* Use traditional groups to avoid HRR from key share mismatch */
2666+
ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 2), WOLFSSL_SUCCESS);
2667+
/* Disable HRR cookie so the server directly sends a ServerHello */
2668+
ExpectIntEQ(wolfSSL_disable_hrr_cookie(ssl_s), WOLFSSL_SUCCESS);
2669+
2670+
/* Client sends ClientHello (with non-empty legacy_session_id) */
2671+
ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
2672+
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
2673+
2674+
/* Server processes ClientHello and sends ServerHello + flight */
2675+
ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1);
2676+
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
2677+
2678+
/* Verify the ServerHello on the wire.
2679+
* Layout: DTLS Record Header (13) + DTLS Handshake Header (12) +
2680+
* ProtocolVersion (2) + Random (32) = offset 59 for
2681+
* legacy_session_id_echo length byte. */
2682+
ExpectIntGE(test_ctx.c_len, 60);
2683+
ExpectIntEQ(test_ctx.c_buff[0], handshake);
2684+
ExpectIntEQ(test_ctx.c_buff[DTLS_RECORD_HEADER_SZ], server_hello);
2685+
ExpectIntEQ(test_ctx.c_buff[DTLS_RECORD_HEADER_SZ +
2686+
DTLS_HANDSHAKE_HEADER_SZ + OPAQUE16_LEN + RAN_LEN], 0);
2687+
2688+
/* Complete the handshake */
2689+
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
2690+
2691+
wolfSSL_SESSION_free(sess);
2692+
wolfSSL_free(ssl_c);
2693+
wolfSSL_free(ssl_s);
2694+
wolfSSL_CTX_free(ctx_c);
2695+
wolfSSL_CTX_free(ctx_s);
2696+
#endif
2697+
return EXPECT_RESULT();
2698+
}

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_dtls13_no_session_id_echo(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_dtls13_no_session_id_echo)
8385
#endif /* TESTS_API_DTLS_H */

0 commit comments

Comments
 (0)