@@ -3687,6 +3687,156 @@ int test_tls13_pqc_hybrid_truncated_keyshare(void)
36873687 * (32 bytes) does not cause an unsigned integer underflow / OOB read in
36883688 * SetTicket. Uses a full memio handshake, then injects a crafted
36893689 * NewSessionTicket with a 5-byte ticket into the client's read path. */
3690+ int test_tls13_empty_record_limit (void )
3691+ {
3692+ EXPECT_DECLS ;
3693+ #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && defined(WOLFSSL_TLS13 )
3694+ struct test_memio_ctx test_ctx ;
3695+ WOLFSSL_CTX * ctx_c = NULL , * ctx_s = NULL ;
3696+ WOLFSSL * ssl_c = NULL , * ssl_s = NULL ;
3697+ int recSz ;
3698+ int numRecs = WOLFSSL_MAX_EMPTY_RECORDS + 1 ;
3699+ byte rec [128 ]; /* buffer for one encrypted record */
3700+ byte * allRecs = NULL ;
3701+ int i ;
3702+ char buf [64 ];
3703+
3704+ /* Test 1: Exceeding the empty record limit returns an error. */
3705+ XMEMSET (& test_ctx , 0 , sizeof (test_ctx ));
3706+ ExpectIntEQ (test_memio_setup (& test_ctx , & ctx_c , & ctx_s , & ssl_c , & ssl_s ,
3707+ wolfTLSv1_3_client_method , wolfTLSv1_3_server_method ), 0 );
3708+
3709+ ExpectIntEQ (test_memio_do_handshake (ssl_c , ssl_s , 10 , NULL ), 0 );
3710+
3711+ /* Consume any post-handshake messages (e.g. NewSessionTicket). */
3712+ wolfSSL_read (ssl_c , buf , sizeof (buf ));
3713+ test_memio_clear_buffer (& test_ctx , 0 );
3714+ test_memio_clear_buffer (& test_ctx , 1 );
3715+
3716+ /* Get the size of an encrypted zero-length app data record. */
3717+ recSz = BuildTls13Message (ssl_c , NULL , 0 , NULL , 0 ,
3718+ application_data , 0 , 1 , 0 );
3719+ ExpectIntGT (recSz , 0 );
3720+ ExpectIntLE (recSz , (int )sizeof (rec ));
3721+
3722+ /* Build all empty records into one contiguous buffer. */
3723+ if (EXPECT_SUCCESS ()) {
3724+ allRecs = (byte * )XMALLOC ((size_t )(recSz * numRecs ), NULL ,
3725+ DYNAMIC_TYPE_TMP_BUFFER );
3726+ ExpectNotNull (allRecs );
3727+ }
3728+
3729+ for (i = 0 ; i < numRecs && EXPECT_SUCCESS (); i ++ ) {
3730+ XMEMSET (rec , 0 , sizeof (rec ));
3731+ ExpectIntEQ (BuildTls13Message (ssl_c , rec , (int )sizeof (rec ), rec +
3732+ RECORD_HEADER_SZ , 0 , application_data , 0 , 0 , 0 ),
3733+ recSz );
3734+ XMEMCPY (allRecs + i * recSz , rec , (size_t )recSz );
3735+ }
3736+
3737+ /* Inject all records as a single message. */
3738+ ExpectIntEQ (test_memio_inject_message (& test_ctx , 0 , (const char * )allRecs ,
3739+ recSz * numRecs ), 0 );
3740+
3741+ /* The server's wolfSSL_read should fail with EMPTY_RECORD_LIMIT_E. */
3742+ ExpectIntEQ (wolfSSL_read (ssl_s , buf , sizeof (buf )),
3743+ WC_NO_ERR_TRACE (WOLFSSL_FATAL_ERROR ));
3744+ ExpectIntEQ (wolfSSL_get_error (ssl_s , WOLFSSL_FATAL_ERROR ),
3745+ WC_NO_ERR_TRACE (EMPTY_RECORD_LIMIT_E ));
3746+
3747+ XFREE (allRecs , NULL , DYNAMIC_TYPE_TMP_BUFFER );
3748+ allRecs = NULL ;
3749+ wolfSSL_free (ssl_c );
3750+ ssl_c = NULL ;
3751+ wolfSSL_free (ssl_s );
3752+ ssl_s = NULL ;
3753+ wolfSSL_CTX_free (ctx_c );
3754+ ctx_c = NULL ;
3755+ wolfSSL_CTX_free (ctx_s );
3756+ ctx_s = NULL ;
3757+
3758+ /* Test 2: Counter resets on non-empty record.
3759+ * Send (limit - 1) empty records, then 1 non-empty, then (limit - 1)
3760+ * more empty records. Should succeed without hitting the limit. */
3761+ XMEMSET (& test_ctx , 0 , sizeof (test_ctx ));
3762+ ExpectIntEQ (test_memio_setup (& test_ctx , & ctx_c , & ctx_s , & ssl_c , & ssl_s ,
3763+ wolfTLSv1_3_client_method , wolfTLSv1_3_server_method ), 0 );
3764+
3765+ ExpectIntEQ (test_memio_do_handshake (ssl_c , ssl_s , 10 , NULL ), 0 );
3766+
3767+ wolfSSL_read (ssl_c , buf , sizeof (buf ));
3768+ test_memio_clear_buffer (& test_ctx , 0 );
3769+ test_memio_clear_buffer (& test_ctx , 1 );
3770+
3771+ recSz = BuildTls13Message (ssl_c , NULL , 0 , NULL , 0 ,
3772+ application_data , 0 , 1 , 0 );
3773+ ExpectIntGT (recSz , 0 );
3774+
3775+ {
3776+ int emptyBefore = WOLFSSL_MAX_EMPTY_RECORDS - 1 ;
3777+ int emptyAfter = WOLFSSL_MAX_EMPTY_RECORDS - 1 ;
3778+ int dataRecSz ;
3779+ byte dataRec [128 ];
3780+ byte payload [1 ] = { 'a' };
3781+ int totalSz ;
3782+
3783+ dataRecSz = BuildTls13Message (ssl_c , NULL , 0 , NULL , 1 ,
3784+ application_data , 0 , 1 , 0 );
3785+ ExpectIntGT (dataRecSz , 0 );
3786+
3787+ totalSz = recSz * (emptyBefore + emptyAfter ) + dataRecSz ;
3788+ if (EXPECT_SUCCESS ()) {
3789+ allRecs = (byte * )XMALLOC ((size_t )totalSz , NULL ,
3790+ DYNAMIC_TYPE_TMP_BUFFER );
3791+ ExpectNotNull (allRecs );
3792+ }
3793+
3794+ /* Build (limit - 1) empty records */
3795+ for (i = 0 ; i < emptyBefore && EXPECT_SUCCESS (); i ++ ) {
3796+ XMEMSET (rec , 0 , sizeof (rec ));
3797+ ExpectIntEQ (BuildTls13Message (ssl_c , rec , (int )sizeof (rec ),
3798+ rec + RECORD_HEADER_SZ , 0 , application_data ,
3799+ 0 , 0 , 0 ), recSz );
3800+ XMEMCPY (allRecs + i * recSz , rec , (size_t )recSz );
3801+ }
3802+
3803+ /* Build 1 non-empty record */
3804+ XMEMSET (dataRec , 0 , sizeof (dataRec ));
3805+ XMEMCPY (dataRec + RECORD_HEADER_SZ , payload , sizeof (payload ));
3806+ ExpectIntEQ (BuildTls13Message (ssl_c , dataRec , (int )sizeof (dataRec ),
3807+ dataRec + RECORD_HEADER_SZ , 1 , application_data ,
3808+ 0 , 0 , 0 ), dataRecSz );
3809+ XMEMCPY (allRecs + emptyBefore * recSz , dataRec , (size_t )dataRecSz );
3810+
3811+ /* Build (limit - 1) more empty records */
3812+ for (i = 0 ; i < emptyAfter && EXPECT_SUCCESS (); i ++ ) {
3813+ XMEMSET (rec , 0 , sizeof (rec ));
3814+ ExpectIntEQ (BuildTls13Message (ssl_c , rec , (int )sizeof (rec ),
3815+ rec + RECORD_HEADER_SZ , 0 , application_data ,
3816+ 0 , 0 , 0 ), recSz );
3817+ XMEMCPY (allRecs + emptyBefore * recSz + dataRecSz + i * recSz ,
3818+ rec , (size_t )recSz );
3819+ }
3820+
3821+ ExpectIntEQ (test_memio_inject_message (& test_ctx , 0 ,
3822+ (const char * )allRecs , totalSz ), 0 );
3823+ }
3824+
3825+ /* wolfSSL_read should return the 1-byte payload. The counter resets
3826+ * on the non-empty record so neither batch of (limit - 1) empties
3827+ * triggers the error. */
3828+ ExpectIntEQ (wolfSSL_read (ssl_s , buf , sizeof (buf )), 1 );
3829+ ExpectIntEQ (buf [0 ], 'a' );
3830+
3831+ XFREE (allRecs , NULL , DYNAMIC_TYPE_TMP_BUFFER );
3832+ wolfSSL_free (ssl_c );
3833+ wolfSSL_free (ssl_s );
3834+ wolfSSL_CTX_free (ctx_c );
3835+ wolfSSL_CTX_free (ctx_s );
3836+ #endif
3837+ return EXPECT_RESULT ();
3838+ }
3839+
36903840int test_tls13_short_session_ticket (void )
36913841{
36923842 EXPECT_DECLS ;
0 commit comments