mbedTLS Client Accepts an Over-Limit NewSessionTicket.extensions Vector
Summary
This is a confirmed protocol-compliance issue in the TLS 1.3 client receive path. The corrected syntax limits NewSessionTicket.extensions to Extension extensions<0..2^16-2>, so the encoded vector length cannot exceed 65534. mbedTLS treats the field as an unrestricted 16-bit length and checks only whether the message buffer contains that many bytes. A focused runtime harness confirmed that the parser accepts both the legal boundary 65534 and the first illegal value 65535.
Standard Requirement
struct {
uint32 ticket_lifetime;
uint32 ticket_age_add;
opaque ticket_nonce<0..255>;
opaque ticket<1..2^16-1>;
Extension extensions<0..2^16-2>;
} NewSessionTicket;
RFC 9846 identifies this upper bound as a correction rather than an implementation choice:
* Correct the upper bound on NewSessionTicket.extensions. This was
a calculation error.
RFC 8446 also states:
In TLS 1.3, a client receiving a CertificateRequest or
NewSessionTicket MUST also ignore all unrecognized extensions.
Together, these rules mean that the legal encoded length is 0..65534, that 65535 is invalid, and that filling a test vector with an unknown extension does not create a separate violation because a client must ignore unknown NewSessionTicket extensions.
Relevant Source Code
library/ssl_tls13_client.c:2926
MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2);
extensions_len = MBEDTLS_GET_UINT16_BE(p, 0);
p += 2;
MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, extensions_len);
MBEDTLS_SSL_DEBUG_BUF(3, "ticket extension", p, extensions_len);
ret = ssl_tls13_parse_new_session_ticket_exts(ssl, p, p + extensions_len);
The parser reads an ordinary uint16 and verifies only that the buffer contains extensions_len bytes. It does not enforce extensions_len <= 65534.
library/ssl_misc.h:198
#define MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_NST \
(MBEDTLS_SSL_EXT_MASK(EARLY_DATA) | \
MBEDTLS_SSL_TLS1_3_EXT_MASK_UNRECOGNIZED)
The implementation explicitly permits unrecognized extension types in a NewSessionTicket.
library/ssl_tls13_client.c:2792
while (p < end) {
unsigned int extension_type;
size_t extension_data_len;
int ret;
MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 4);
extension_type = MBEDTLS_GET_UINT16_BE(p, 0);
extension_data_len = MBEDTLS_GET_UINT16_BE(p, 2);
p += 4;
MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, extension_data_len);
ret = mbedtls_ssl_tls13_check_received_extension(
ssl, MBEDTLS_SSL_HS_NEW_SESSION_TICKET, extension_type,
MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_NST);
if (ret != 0) {
return ret;
}
switch (extension_type) {
/* ... */
default:
MBEDTLS_SSL_PRINT_EXT(
3, MBEDTLS_SSL_HS_NEW_SESSION_TICKET,
extension_type, "( ignored )");
break;
}
p += extension_data_len;
}
The inner extension parser imposes no additional outer-vector bound and ignores unknown types.
Implementation Behavior
The receive path continues whenever the outer extensions_len fits in a uint16 and the buffer contains that many bytes. Unknown inner extensions are allowed and ignored. Consequently, a structurally self-consistent vector with the illegal total length 65535 is accepted. Using one unknown extension to fill the vector isolates the outer-length check because the extension type itself is valid input for this message.
Inconsistency Reason
RFC 8446 as corrected by RFC 9846 restricts NewSessionTicket.extensions to 0..65534. mbedTLS accepts 65535. This is a missing receive-side protocol constraint, not a documentation ambiguity: the standard rejects the first over-limit boundary, while the implementation parses it successfully.
Runtime Evidence
A harness compiled the TLS 1.3 client parser into the test translation unit and directly exercised ssl_tls13_parse_new_session_ticket(). It constructed two otherwise identical messages:
control_valid_max used extensions_len = 65534.
probe_over_limit used extensions_len = 65535.
Both messages used a zero-length ticket nonce, a one-byte ticket, and one unknown extension whose data length exactly filled the outer vector. Thus, the probe's only invalid property was exceeding the corrected vector bound by one byte.
The observed output was:
control_valid_max extensions_len=65534 ret=0 accepted ticket_nonce_len=0
probe_over_limit extensions_len=65535 ret=0 accepted ticket_nonce_len=0
The legal boundary succeeded, validating the harness and target path. The illegal boundary also returned 0, proving that the current parser accepts the over-limit value at runtime.
Impact
The client accepts a NewSessionTicket that violates the formal TLS 1.3 structure and is therefore more permissive than the standard. The available evidence does not show an out-of-bounds access, crash, remote-code execution, or key disclosure; the direct impact is protocol-parser correctness and incomplete input validation.
Fix Direction
- Reject
extensions_len > 65534 immediately after reading the field in library/ssl_tls13_client.c.
- Perform the check before calling
ssl_tls13_parse_new_session_ticket_exts().
- Add regression cases where
65534 succeeds and 65535 fails.
- Scope the fix to the client receive path established by this report; no conclusion is made here about the server send path.
mbedTLS Client Accepts an Over-Limit
NewSessionTicket.extensionsVectorSummary
This is a confirmed protocol-compliance issue in the TLS 1.3 client receive path. The corrected syntax limits
NewSessionTicket.extensionstoExtension extensions<0..2^16-2>, so the encoded vector length cannot exceed65534. mbedTLS treats the field as an unrestricted 16-bit length and checks only whether the message buffer contains that many bytes. A focused runtime harness confirmed that the parser accepts both the legal boundary65534and the first illegal value65535.Standard Requirement
RFC 9846 identifies this upper bound as a correction rather than an implementation choice:
RFC 8446 also states:
Together, these rules mean that the legal encoded length is
0..65534, that65535is invalid, and that filling a test vector with an unknown extension does not create a separate violation because a client must ignore unknownNewSessionTicketextensions.Relevant Source Code
library/ssl_tls13_client.c:2926The parser reads an ordinary
uint16and verifies only that the buffer containsextensions_lenbytes. It does not enforceextensions_len <= 65534.library/ssl_misc.h:198The implementation explicitly permits unrecognized extension types in a
NewSessionTicket.library/ssl_tls13_client.c:2792The inner extension parser imposes no additional outer-vector bound and ignores unknown types.
Implementation Behavior
The receive path continues whenever the outer
extensions_lenfits in auint16and the buffer contains that many bytes. Unknown inner extensions are allowed and ignored. Consequently, a structurally self-consistent vector with the illegal total length65535is accepted. Using one unknown extension to fill the vector isolates the outer-length check because the extension type itself is valid input for this message.Inconsistency Reason
RFC 8446 as corrected by RFC 9846 restricts
NewSessionTicket.extensionsto0..65534. mbedTLS accepts65535. This is a missing receive-side protocol constraint, not a documentation ambiguity: the standard rejects the first over-limit boundary, while the implementation parses it successfully.Runtime Evidence
A harness compiled the TLS 1.3 client parser into the test translation unit and directly exercised
ssl_tls13_parse_new_session_ticket(). It constructed two otherwise identical messages:control_valid_maxusedextensions_len = 65534.probe_over_limitusedextensions_len = 65535.Both messages used a zero-length ticket nonce, a one-byte ticket, and one unknown extension whose data length exactly filled the outer vector. Thus, the probe's only invalid property was exceeding the corrected vector bound by one byte.
The observed output was:
The legal boundary succeeded, validating the harness and target path. The illegal boundary also returned
0, proving that the current parser accepts the over-limit value at runtime.Impact
The client accepts a
NewSessionTicketthat violates the formal TLS 1.3 structure and is therefore more permissive than the standard. The available evidence does not show an out-of-bounds access, crash, remote-code execution, or key disclosure; the direct impact is protocol-parser correctness and incomplete input validation.Fix Direction
extensions_len > 65534immediately after reading the field inlibrary/ssl_tls13_client.c.ssl_tls13_parse_new_session_ticket_exts().65534succeeds and65535fails.