@@ -3779,6 +3779,156 @@ int test_tls13_pqc_hybrid_truncated_keyshare(void)
37793779 * (32 bytes) does not cause an unsigned integer underflow / OOB read in
37803780 * SetTicket. Uses a full memio handshake, then injects a crafted
37813781 * NewSessionTicket with a 5-byte ticket into the client's read path. */
3782+ int test_tls13_empty_record_limit (void )
3783+ {
3784+ EXPECT_DECLS ;
3785+ #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && defined(WOLFSSL_TLS13 )
3786+ struct test_memio_ctx test_ctx ;
3787+ WOLFSSL_CTX * ctx_c = NULL , * ctx_s = NULL ;
3788+ WOLFSSL * ssl_c = NULL , * ssl_s = NULL ;
3789+ int recSz ;
3790+ int numRecs = WOLFSSL_MAX_EMPTY_RECORDS + 1 ;
3791+ byte rec [128 ]; /* buffer for one encrypted record */
3792+ byte * allRecs = NULL ;
3793+ int i ;
3794+ char buf [64 ];
3795+
3796+ /* Test 1: Exceeding the empty record limit returns an error. */
3797+ XMEMSET (& test_ctx , 0 , sizeof (test_ctx ));
3798+ ExpectIntEQ (test_memio_setup (& test_ctx , & ctx_c , & ctx_s , & ssl_c , & ssl_s ,
3799+ wolfTLSv1_3_client_method , wolfTLSv1_3_server_method ), 0 );
3800+
3801+ ExpectIntEQ (test_memio_do_handshake (ssl_c , ssl_s , 10 , NULL ), 0 );
3802+
3803+ /* Consume any post-handshake messages (e.g. NewSessionTicket). */
3804+ wolfSSL_read (ssl_c , buf , sizeof (buf ));
3805+ test_memio_clear_buffer (& test_ctx , 0 );
3806+ test_memio_clear_buffer (& test_ctx , 1 );
3807+
3808+ /* Get the size of an encrypted zero-length app data record. */
3809+ recSz = BuildTls13Message (ssl_c , NULL , 0 , NULL , 0 ,
3810+ application_data , 0 , 1 , 0 );
3811+ ExpectIntGT (recSz , 0 );
3812+ ExpectIntLE (recSz , (int )sizeof (rec ));
3813+
3814+ /* Build all empty records into one contiguous buffer. */
3815+ if (EXPECT_SUCCESS ()) {
3816+ allRecs = (byte * )XMALLOC ((size_t )(recSz * numRecs ), NULL ,
3817+ DYNAMIC_TYPE_TMP_BUFFER );
3818+ ExpectNotNull (allRecs );
3819+ }
3820+
3821+ for (i = 0 ; i < numRecs && EXPECT_SUCCESS (); i ++ ) {
3822+ XMEMSET (rec , 0 , sizeof (rec ));
3823+ ExpectIntEQ (BuildTls13Message (ssl_c , rec , (int )sizeof (rec ), rec +
3824+ RECORD_HEADER_SZ , 0 , application_data , 0 , 0 , 0 ),
3825+ recSz );
3826+ XMEMCPY (allRecs + i * recSz , rec , (size_t )recSz );
3827+ }
3828+
3829+ /* Inject all records as a single message. */
3830+ ExpectIntEQ (test_memio_inject_message (& test_ctx , 0 , (const char * )allRecs ,
3831+ recSz * numRecs ), 0 );
3832+
3833+ /* The server's wolfSSL_read should fail with EMPTY_RECORD_LIMIT_E. */
3834+ ExpectIntEQ (wolfSSL_read (ssl_s , buf , sizeof (buf )),
3835+ WC_NO_ERR_TRACE (WOLFSSL_FATAL_ERROR ));
3836+ ExpectIntEQ (wolfSSL_get_error (ssl_s , WOLFSSL_FATAL_ERROR ),
3837+ WC_NO_ERR_TRACE (EMPTY_RECORD_LIMIT_E ));
3838+
3839+ XFREE (allRecs , NULL , DYNAMIC_TYPE_TMP_BUFFER );
3840+ allRecs = NULL ;
3841+ wolfSSL_free (ssl_c );
3842+ ssl_c = NULL ;
3843+ wolfSSL_free (ssl_s );
3844+ ssl_s = NULL ;
3845+ wolfSSL_CTX_free (ctx_c );
3846+ ctx_c = NULL ;
3847+ wolfSSL_CTX_free (ctx_s );
3848+ ctx_s = NULL ;
3849+
3850+ /* Test 2: Counter resets on non-empty record.
3851+ * Send (limit - 1) empty records, then 1 non-empty, then (limit - 1)
3852+ * more empty records. Should succeed without hitting the limit. */
3853+ XMEMSET (& test_ctx , 0 , sizeof (test_ctx ));
3854+ ExpectIntEQ (test_memio_setup (& test_ctx , & ctx_c , & ctx_s , & ssl_c , & ssl_s ,
3855+ wolfTLSv1_3_client_method , wolfTLSv1_3_server_method ), 0 );
3856+
3857+ ExpectIntEQ (test_memio_do_handshake (ssl_c , ssl_s , 10 , NULL ), 0 );
3858+
3859+ wolfSSL_read (ssl_c , buf , sizeof (buf ));
3860+ test_memio_clear_buffer (& test_ctx , 0 );
3861+ test_memio_clear_buffer (& test_ctx , 1 );
3862+
3863+ recSz = BuildTls13Message (ssl_c , NULL , 0 , NULL , 0 ,
3864+ application_data , 0 , 1 , 0 );
3865+ ExpectIntGT (recSz , 0 );
3866+
3867+ {
3868+ int emptyBefore = WOLFSSL_MAX_EMPTY_RECORDS - 1 ;
3869+ int emptyAfter = WOLFSSL_MAX_EMPTY_RECORDS - 1 ;
3870+ int dataRecSz ;
3871+ byte dataRec [128 ];
3872+ byte payload [1 ] = { 'a' };
3873+ int totalSz ;
3874+
3875+ dataRecSz = BuildTls13Message (ssl_c , NULL , 0 , NULL , 1 ,
3876+ application_data , 0 , 1 , 0 );
3877+ ExpectIntGT (dataRecSz , 0 );
3878+
3879+ totalSz = recSz * (emptyBefore + emptyAfter ) + dataRecSz ;
3880+ if (EXPECT_SUCCESS ()) {
3881+ allRecs = (byte * )XMALLOC ((size_t )totalSz , NULL ,
3882+ DYNAMIC_TYPE_TMP_BUFFER );
3883+ ExpectNotNull (allRecs );
3884+ }
3885+
3886+ /* Build (limit - 1) empty records */
3887+ for (i = 0 ; i < emptyBefore && EXPECT_SUCCESS (); i ++ ) {
3888+ XMEMSET (rec , 0 , sizeof (rec ));
3889+ ExpectIntEQ (BuildTls13Message (ssl_c , rec , (int )sizeof (rec ),
3890+ rec + RECORD_HEADER_SZ , 0 , application_data ,
3891+ 0 , 0 , 0 ), recSz );
3892+ XMEMCPY (allRecs + i * recSz , rec , (size_t )recSz );
3893+ }
3894+
3895+ /* Build 1 non-empty record */
3896+ XMEMSET (dataRec , 0 , sizeof (dataRec ));
3897+ XMEMCPY (dataRec + RECORD_HEADER_SZ , payload , sizeof (payload ));
3898+ ExpectIntEQ (BuildTls13Message (ssl_c , dataRec , (int )sizeof (dataRec ),
3899+ dataRec + RECORD_HEADER_SZ , 1 , application_data ,
3900+ 0 , 0 , 0 ), dataRecSz );
3901+ XMEMCPY (allRecs + emptyBefore * recSz , dataRec , (size_t )dataRecSz );
3902+
3903+ /* Build (limit - 1) more empty records */
3904+ for (i = 0 ; i < emptyAfter && EXPECT_SUCCESS (); i ++ ) {
3905+ XMEMSET (rec , 0 , sizeof (rec ));
3906+ ExpectIntEQ (BuildTls13Message (ssl_c , rec , (int )sizeof (rec ),
3907+ rec + RECORD_HEADER_SZ , 0 , application_data ,
3908+ 0 , 0 , 0 ), recSz );
3909+ XMEMCPY (allRecs + emptyBefore * recSz + dataRecSz + i * recSz ,
3910+ rec , (size_t )recSz );
3911+ }
3912+
3913+ ExpectIntEQ (test_memio_inject_message (& test_ctx , 0 ,
3914+ (const char * )allRecs , totalSz ), 0 );
3915+ }
3916+
3917+ /* wolfSSL_read should return the 1-byte payload. The counter resets
3918+ * on the non-empty record so neither batch of (limit - 1) empties
3919+ * triggers the error. */
3920+ ExpectIntEQ (wolfSSL_read (ssl_s , buf , sizeof (buf )), 1 );
3921+ ExpectIntEQ (buf [0 ], 'a' );
3922+
3923+ XFREE (allRecs , NULL , DYNAMIC_TYPE_TMP_BUFFER );
3924+ wolfSSL_free (ssl_c );
3925+ wolfSSL_free (ssl_s );
3926+ wolfSSL_CTX_free (ctx_c );
3927+ wolfSSL_CTX_free (ctx_s );
3928+ #endif
3929+ return EXPECT_RESULT ();
3930+ }
3931+
37823932int test_tls13_short_session_ticket (void )
37833933{
37843934 EXPECT_DECLS ;
0 commit comments