Skip to content

Commit 31ec469

Browse files
committed
Enforce empty legacy_session_id in DTLS 1.3 per RFC 9147
- Update DTLS 1.3 to ensure the legacy_session_id field is always empty in ServerHello and HelloRetryRequest messages, following RFC 9147 Section 5.3. - Disable middlebox compatibility mode per RFC 9147 Section 5. - Always check the version when resuming a session Fixes ZD 21376.
1 parent 7efc962 commit 31ec469

9 files changed

Lines changed: 162 additions & 47 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/ssl_sess.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,12 +1553,9 @@ int wolfSSL_SetSession(WOLFSSL* ssl, WOLFSSL_SESSION* session)
15531553
ssl->options.resuming = 1;
15541554
ssl->options.haveEMS = (ssl->session->haveEMS) ? 1 : 0;
15551555

1556-
#if defined(SESSION_CERTS) || (defined(WOLFSSL_TLS13) && \
1557-
defined(HAVE_SESSION_TICKET))
15581556
ssl->version = ssl->session->version;
15591557
if (IsAtLeastTLSv1_3(ssl->version))
15601558
ssl->options.tls1_3 = 1;
1561-
#endif
15621559
#if defined(SESSION_CERTS) || !defined(NO_RESUME_SUITE_CHECK) || \
15631560
(defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET))
15641561
ssl->options.cipherSuite0 = ssl->session->cipherSuite0;
@@ -3176,10 +3173,8 @@ static void SESSION_ex_data_cache_update(WOLFSSL_SESSION* session, int idx,
31763173
if (cacheSession && cacheSession->sessionIDSz == ID_LEN &&
31773174
XMEMCMP(id, cacheSession->sessionID, ID_LEN) == 0
31783175
&& session->side == cacheSession->side
3179-
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)
31803176
&& (IsAtLeastTLSv1_3(session->version) ==
31813177
IsAtLeastTLSv1_3(cacheSession->version))
3182-
#endif
31833178
) {
31843179
if (get) {
31853180
if (getRet) {
@@ -3604,10 +3599,7 @@ void SetupSession(WOLFSSL* ssl)
36043599
#ifndef NO_ASN_TIME
36053600
session->bornOn = LowResTimer();
36063601
#endif
3607-
#if defined(SESSION_CERTS) || (defined(WOLFSSL_TLS13) && \
3608-
defined(HAVE_SESSION_TICKET))
36093602
session->version = ssl->version;
3610-
#endif
36113603
#if defined(SESSION_CERTS) || !defined(NO_RESUME_SUITE_CHECK) || \
36123604
(defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET))
36133605
session->cipherSuite0 = ssl->options.cipherSuite0;

src/tls.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12173,7 +12173,6 @@ static int TLSX_PreSharedKey_Parse(WOLFSSL* ssl, const byte* input,
1217312173
}
1217412174
list->chosen = 1;
1217512175

12176-
#ifdef HAVE_SESSION_TICKET
1217712176
if (list->resumption) {
1217812177
/* Check that the session's details are the same as the server's. */
1217912178
if (ssl->options.cipherSuite0 != ssl->session->cipherSuite0 ||
@@ -12184,7 +12183,6 @@ static int TLSX_PreSharedKey_Parse(WOLFSSL* ssl, const byte* input,
1218412183
return PSK_KEY_ERROR;
1218512184
}
1218612185
}
12187-
#endif
1218812186

1218912187
return 0;
1219012188
}

src/tls13.c

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4570,7 +4570,6 @@ int SendTls13ClientHello(WOLFSSL* ssl)
45704570
}
45714571
#endif /* WOLFSSL_DTLS */
45724572

4573-
#ifdef HAVE_SESSION_TICKET
45744573
if (ssl->options.resuming &&
45754574
(ssl->session->version.major != ssl->version.major ||
45764575
ssl->session->version.minor != ssl->version.minor)) {
@@ -4590,7 +4589,6 @@ int SendTls13ClientHello(WOLFSSL* ssl)
45904589
return VERSION_ERROR;
45914590
}
45924591
}
4593-
#endif
45944592

45954593
suites = WOLFSSL_SUITES(ssl);
45964594
if (suites == NULL) {
@@ -4644,6 +4642,13 @@ int SendTls13ClientHello(WOLFSSL* ssl)
46444642
ssl->session->sessionIDSz = 0;
46454643
ssl->options.tls13MiddleBoxCompat = 0;
46464644
}
4645+
#endif
4646+
#ifdef WOLFSSL_DTLS13
4647+
if (ssl->options.dtls) {
4648+
/* RFC 9147 Section 5: DTLS implementations do not use the
4649+
* TLS 1.3 "compatibility mode" */
4650+
ssl->options.tls13MiddleBoxCompat = 0;
4651+
}
46474652
#endif
46484653
GetTls13SessionId(ssl, NULL, &sessIdSz);
46494654
args->length += (word16)sessIdSz;
@@ -5587,16 +5592,25 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
55875592
}
55885593
else
55895594
#endif /* WOLFSSL_TLS13_MIDDLEBOX_COMPAT */
5595+
#if defined(WOLFSSL_QUIC) || defined(WOLFSSL_DTLS13)
5596+
if (0
55905597
#ifdef WOLFSSL_QUIC
5591-
if (WOLFSSL_IS_QUIC(ssl)) {
5598+
|| WOLFSSL_IS_QUIC(ssl)
5599+
#endif
5600+
#ifdef WOLFSSL_DTLS13
5601+
|| ssl->options.dtls
5602+
#endif
5603+
) {
5604+
/* RFC 9147 Section 5.3 / RFC 9001 Section 8.4: DTLS 1.3 and QUIC
5605+
* ServerHello must have empty legacy_session_id_echo. */
55925606
if (args->sessIdSz != 0) {
55935607
WOLFSSL_MSG("args->sessIdSz != 0");
55945608
WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
55955609
return INVALID_PARAMETER;
55965610
}
55975611
}
55985612
else
5599-
#endif /* WOLFSSL_QUIC */
5613+
#endif /* WOLFSSL_QUIC || WOLFSSL_DTLS13 */
56005614
if (args->sessIdSz != ssl->session->sessionIDSz || (args->sessIdSz > 0 &&
56015615
XMEMCMP(ssl->session->sessionID, args->sessId, args->sessIdSz) != 0))
56025616
{
@@ -6559,6 +6573,7 @@ static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie)
65596573
word16 length;
65606574
int keyShareExt = 0;
65616575
int ret;
6576+
byte sessIdSz;
65626577

65636578
ret = TlsCheckCookie(ssl, cookie->data, (byte)cookie->len);
65646579
if (ret < 0)
@@ -6583,7 +6598,13 @@ static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie)
65836598
return ret;
65846599

65856600
/* Reconstruct the HelloRetryMessage for handshake hash. */
6586-
length = HRR_BODY_SZ - ID_LEN + ssl->session->sessionIDSz +
6601+
sessIdSz = ssl->session->sessionIDSz;
6602+
#ifdef WOLFSSL_DTLS13
6603+
/* RFC 9147 Section 5.3: DTLS 1.3 must use empty legacy_session_id. */
6604+
if (ssl->options.dtls)
6605+
sessIdSz = 0;
6606+
#endif
6607+
length = HRR_BODY_SZ - ID_LEN + sessIdSz +
65876608
HRR_COOKIE_HDR_SZ + cookie->len;
65886609
length += HRR_VERSIONS_SZ;
65896610
/* HashSz (1 byte) + Hash (HashSz bytes) + CipherSuite (2 bytes) */
@@ -6610,10 +6631,10 @@ static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie)
66106631
XMEMCPY(hrr + hrrIdx, helloRetryRequestRandom, RAN_LEN);
66116632
hrrIdx += RAN_LEN;
66126633

6613-
hrr[hrrIdx++] = ssl->session->sessionIDSz;
6614-
if (ssl->session->sessionIDSz > 0) {
6615-
XMEMCPY(hrr + hrrIdx, ssl->session->sessionID, ssl->session->sessionIDSz);
6616-
hrrIdx += ssl->session->sessionIDSz;
6634+
hrr[hrrIdx++] = sessIdSz;
6635+
if (sessIdSz > 0) {
6636+
XMEMCPY(hrr + hrrIdx, ssl->session->sessionID, sessIdSz);
6637+
hrrIdx += sessIdSz;
66176638
}
66186639

66196640
/* Restore the cipher suite from the cookie. */
@@ -6626,7 +6647,7 @@ static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie)
66266647
hrr[hrrIdx++] = 0;
66276648

66286649
/* Extensions' length */
6629-
length -= HRR_BODY_SZ - ID_LEN + ssl->session->sessionIDSz;
6650+
length -= HRR_BODY_SZ - ID_LEN + sessIdSz;
66306651
c16toa(length, hrr + hrrIdx);
66316652
hrrIdx += 2;
66326653

@@ -7051,9 +7072,20 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
70517072
if (sessIdSz + args->idx > helloSz)
70527073
ERROR_OUT(BUFFER_ERROR, exit_dch);
70537074

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

70597091
#ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
@@ -7626,10 +7658,21 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType)
76267658
WOLFSSL_BUFFER(ssl->arrays->serverRandom, RAN_LEN);
76277659
#endif
76287660

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

76357678
/* Chosen cipher suite */

tests/api.c

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4559,12 +4559,12 @@ static WC_INLINE int test_ssl_memio_write_cb(WOLFSSL *ssl, char *data, int sz,
45594559
* "Import from Hex Dump..." option ion and selecting the TCP
45604560
* encapsulation option. */
45614561
char dump_file_name[64];
4562-
WOLFSSL_BIO *dump_file;
4562+
XFILE dump_file;
45634563
sprintf(dump_file_name, "%s/%s.dump", tmpDirName, currentTestName);
4564-
dump_file = wolfSSL_BIO_new_file(dump_file_name, "a");
4565-
if (dump_file != NULL) {
4566-
(void)wolfSSL_BIO_write(dump_file, data, sz);
4567-
wolfSSL_BIO_free(dump_file);
4564+
dump_file = XFOPEN(dump_file_name, "ab");
4565+
if (dump_file != XBADFILE) {
4566+
(void)XFWRITE(data, 1, (size_t)sz, dump_file);
4567+
XFCLOSE(dump_file);
45684568
}
45694569
}
45704570
#endif
@@ -30732,10 +30732,7 @@ static int test_short_session_id_ssl_ready(WOLFSSL* ssl)
3073230732
/* Setup the session to avoid errors */
3073330733
ssl->session->timeout = (word32)-1;
3073430734
ssl->session->side = WOLFSSL_CLIENT_END;
30735-
#if defined(SESSION_CERTS) || (defined(WOLFSSL_TLS13) && \
30736-
defined(HAVE_SESSION_TICKET))
3073730735
ssl->session->version = ssl->version;
30738-
#endif
3073930736
/* Force a short session ID to be sent */
3074030737
ssl->session->sessionIDSz = 4;
3074130738
#ifndef NO_SESSION_CACHE_REF

tests/api/test_dtls.c

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

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 */

tests/utils.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@ int test_memio_write_cb(WOLFSSL *ssl, char *data, int sz, void *ctx)
8080
#ifdef WOLFSSL_DUMP_MEMIO_STREAM
8181
{
8282
char dump_file_name[64];
83-
WOLFSSL_BIO *dump_file;
83+
XFILE dump_file;
8484
sprintf(dump_file_name, "%s/%s.dump", tmpDirName, currentTestName);
85-
dump_file = wolfSSL_BIO_new_file(dump_file_name, "a");
86-
if (dump_file != NULL) {
87-
(void)wolfSSL_BIO_write(dump_file, data, sz);
88-
wolfSSL_BIO_free(dump_file);
85+
dump_file = XFOPEN(dump_file_name, "ab");
86+
if (dump_file != XBADFILE) {
87+
(void)XFWRITE(data, 1, (size_t)sz, dump_file);
88+
XFCLOSE(dump_file);
8989
}
9090
}
9191
#endif

wolfssl/internal.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4692,10 +4692,7 @@ struct WOLFSSL_SESSION {
46924692
#if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA)
46934693
WOLFSSL_X509* peer; /* peer cert */
46944694
#endif
4695-
#if defined(SESSION_CERTS) || (defined(WOLFSSL_TLS13) && \
4696-
defined(HAVE_SESSION_TICKET))
46974695
ProtocolVersion version; /* which version was used */
4698-
#endif
46994696
#if defined(SESSION_CERTS) || !defined(NO_RESUME_SUITE_CHECK) || \
47004697
(defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET))
47014698
byte cipherSuite0; /* first byte, normally 0 */

0 commit comments

Comments
 (0)