@@ -6310,3 +6310,216 @@ int test_tls13_cipher_fuzz_aes128_ccm_8_sha256(void)
63106310#endif
63116311 return EXPECT_RESULT ();
63126312}
6313+
6314+ /* Regression test for the AEAD record-protection limit constants in
6315+ * internal.h. The macros expand to w64From32(hi, lo). A prior version split
6316+ * the intended 32-bit constants into 16-bit halves and passed each half as
6317+ * a separate 32-bit argument, producing a 64-bit value many orders of
6318+ * magnitude larger than RFC 8446 / RFC 9147 require. That made
6319+ * CheckTLS13AEADSendLimit's key-update trigger effectively unreachable.
6320+ * Compare against the hard-coded spec values so a recurrence is caught even
6321+ * if the macro is reused on both sides of the comparison. */
6322+ int test_tls13_AEAD_limit_macros (void )
6323+ {
6324+ EXPECT_DECLS ;
6325+ #if defined(WOLFSSL_TLS13 ) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS )
6326+ w64wrapper limit ;
6327+
6328+ /* RFC 8446 5.5: 2^24.5 ~= 23726566 (0x016A09E6). */
6329+ limit = AEAD_AES_LIMIT ;
6330+ ExpectIntEQ (w64GetHigh32 (limit ), 0 );
6331+ ExpectIntEQ (w64GetLow32 (limit ), 0x016A09E6 );
6332+
6333+ #ifdef WOLFSSL_DTLS13
6334+ /* RFC 9147 (AES-CCM integrity): 2^23.5 ~= 11863283 (0x00B504F3). */
6335+ limit = DTLS_AEAD_AES_CCM_FAIL_LIMIT ;
6336+ ExpectIntEQ (w64GetHigh32 (limit ), 0 );
6337+ ExpectIntEQ (w64GetLow32 (limit ), 0x00B504F3 );
6338+
6339+ /* Key-update threshold is half the fail limit: 5931641 (0x005A8279). */
6340+ limit = DTLS_AEAD_AES_CCM_FAIL_KU_LIMIT ;
6341+ ExpectIntEQ (w64GetHigh32 (limit ), 0 );
6342+ ExpectIntEQ (w64GetLow32 (limit ), 0x005A8279 );
6343+ #endif
6344+ #endif
6345+ return EXPECT_RESULT ();
6346+ }
6347+
6348+ #if defined(WOLFSSL_TLS13 ) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS ) && \
6349+ !defined(NO_WOLFSSL_CLIENT ) && !defined(NO_WOLFSSL_SERVER ) && \
6350+ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && \
6351+ (defined(BUILD_TLS_AES_128_GCM_SHA256 ) || \
6352+ defined(BUILD_TLS_AES_256_GCM_SHA384 ) || \
6353+ defined(BUILD_TLS_AES_128_CCM_SHA256 ) || \
6354+ defined(BUILD_TLS_AES_128_CCM_8_SHA256 ))
6355+ /* Drive the client's encrypt sequence number towards the spec limit for
6356+ * `suite` and verify CheckTLS13AEADSendLimit's KeyUpdate trigger fires at
6357+ * exactly the right boundary.
6358+ *
6359+ * Two writes are exercised:
6360+ * 1. Counter set to limit - 2. After the write the counter must read
6361+ * limit - 1 (record incremented it by 1) and no KeyUpdate must have
6362+ * been emitted. CheckTLS13AEADSendLimit uses `seq >= limit`, so neither
6363+ * the pre-send check nor the trailing loop check (which runs once more
6364+ * after the last record before wolfSSL_write exits) is allowed to fire.
6365+ * 2. A second write follows with the counter already sitting at limit - 1
6366+ * from the previous record. The user record goes out at seq = limit-1,
6367+ * which bumps the counter to limit; the trailing limit check then
6368+ * fires SendTls13KeyUpdate. SetKeysSide zeroes the encrypt counter, so
6369+ * the post-write counter is 0.
6370+ *
6371+ * With the previous broken AEAD-limit macros the limit was unreachable, no
6372+ * KeyUpdate would ever fire, and the counter would simply advance to
6373+ * limit_lo + 1 in the second case instead of being reset.
6374+ *
6375+ * The AEAD nonce mixes in the record sequence number on both sides, so the
6376+ * server's decrypt counter has to be advanced in lockstep with the client's
6377+ * encrypt counter or the record fails the integrity check. */
6378+ static int test_tls13_AEAD_limit_triggers_KeyUpdate_cs (const char * suite ,
6379+ word32 limit_hi , word32 limit_lo , int expected_bulk_cipher )
6380+ {
6381+ EXPECT_DECLS ;
6382+ struct test_memio_ctx test_ctx ;
6383+ WOLFSSL_CTX * ctx_c = NULL , * ctx_s = NULL ;
6384+ WOLFSSL * ssl_c = NULL , * ssl_s = NULL ;
6385+ const char msg [] = "post-limit-record" ;
6386+ char buf [sizeof (msg )];
6387+ int written ;
6388+
6389+ XMEMSET (& test_ctx , 0 , sizeof (test_ctx ));
6390+ test_ctx .c_ciphers = suite ;
6391+ test_ctx .s_ciphers = suite ;
6392+
6393+ ExpectIntEQ (test_memio_setup (& test_ctx , & ctx_c , & ctx_s , & ssl_c , & ssl_s ,
6394+ wolfTLSv1_3_client_method , wolfTLSv1_3_server_method ), 0 );
6395+ ExpectIntEQ (test_memio_do_handshake (ssl_c , ssl_s , 10 , NULL ), 0 );
6396+
6397+ if (EXPECT_SUCCESS () && ssl_c != NULL && ssl_s != NULL ) {
6398+ /* Sanity check: the negotiated bulk cipher matches what the caller
6399+ * intends to exercise. If a build flag combination falls through to
6400+ * a different suite, the limit constant would be wrong. */
6401+ ExpectIntEQ (ssl_c -> specs .bulk_cipher_algorithm , expected_bulk_cipher );
6402+
6403+ /* Stage the counters two below the limit so the first write stays
6404+ * comfortably below the trigger threshold. */
6405+ ssl_c -> keys .sequence_number_hi = limit_hi ;
6406+ ssl_c -> keys .sequence_number_lo = limit_lo - 2 ;
6407+ ssl_s -> keys .peer_sequence_number_hi = limit_hi ;
6408+ ssl_s -> keys .peer_sequence_number_lo = limit_lo - 2 ;
6409+ }
6410+
6411+ /* First write: below the limit, no KeyUpdate expected. */
6412+ written = wolfSSL_write (ssl_c , msg , (int )sizeof (msg ));
6413+ ExpectIntEQ (written , (int )sizeof (msg ));
6414+
6415+ if (EXPECT_SUCCESS () && ssl_c != NULL ) {
6416+ /* The record bumped the counter from limit-2 to limit-1. A
6417+ * KeyUpdate would have zeroed it via SetKeysSide and bumped to 1. */
6418+ ExpectIntEQ ((int )ssl_c -> keys .sequence_number_hi , (int )limit_hi );
6419+ ExpectIntEQ (ssl_c -> keys .sequence_number_lo , limit_lo - 1 );
6420+ }
6421+
6422+ /* Server consumes the below-limit record with its existing keys. */
6423+ XMEMSET (buf , 0 , sizeof (buf ));
6424+ ExpectIntEQ (wolfSSL_read (ssl_s , buf , (int )sizeof (buf )), (int )sizeof (msg ));
6425+ ExpectIntEQ (XMEMCMP (buf , msg , sizeof (msg )), 0 );
6426+
6427+ /* Second write: the client's counter is now at limit-1. Sending this
6428+ * record will push it to limit, at which point the trailing check
6429+ * inside SendData's loop fires SendTls13KeyUpdate. No manual counter
6430+ * adjustment is needed -- the counter is allowed to "naturally" reach
6431+ * the limit through the previous send. */
6432+ written = wolfSSL_write (ssl_c , msg , (int )sizeof (msg ));
6433+ ExpectIntEQ (written , (int )sizeof (msg ));
6434+
6435+ if (EXPECT_SUCCESS () && ssl_c != NULL ) {
6436+ /* SendTls13KeyUpdate -> DeriveTls13Keys -> SetKeysSide zeroes the
6437+ * encrypt sequence number. The user record went out before the
6438+ * trigger fired, so no record was sent on the new keys. */
6439+ ExpectIntEQ ((int )ssl_c -> keys .sequence_number_hi , 0 );
6440+ ExpectIntEQ ((int )ssl_c -> keys .sequence_number_lo , 0 );
6441+ }
6442+
6443+ /* The server reads the user record (sent under the pre-update keys at
6444+ * seq = limit - 1) before it sees the KeyUpdate record. The KeyUpdate
6445+ * is consumed transparently on a subsequent read; for the test we just
6446+ * need to confirm the user data round-trips. */
6447+ XMEMSET (buf , 0 , sizeof (buf ));
6448+ {
6449+ int r = -1 , attempts ;
6450+ for (attempts = 0 ; attempts < 5 ; attempts ++ ) {
6451+ r = wolfSSL_read (ssl_s , buf , (int )sizeof (buf ));
6452+ if (r > 0 )
6453+ break ;
6454+ if (wolfSSL_get_error (ssl_s , r ) != WOLFSSL_ERROR_WANT_READ )
6455+ break ;
6456+ }
6457+ ExpectIntEQ (r , (int )sizeof (msg ));
6458+ }
6459+ ExpectIntEQ (XMEMCMP (buf , msg , sizeof (msg )), 0 );
6460+
6461+ wolfSSL_free (ssl_c );
6462+ wolfSSL_free (ssl_s );
6463+ wolfSSL_CTX_free (ctx_c );
6464+ wolfSSL_CTX_free (ctx_s );
6465+
6466+ return EXPECT_RESULT ();
6467+ }
6468+ #endif
6469+
6470+ int test_tls13_AEAD_limit_KU_aes128_gcm_sha256 (void )
6471+ {
6472+ EXPECT_DECLS ;
6473+ #if defined(WOLFSSL_TLS13 ) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS ) && \
6474+ !defined(NO_WOLFSSL_CLIENT ) && !defined(NO_WOLFSSL_SERVER ) && \
6475+ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && \
6476+ defined(BUILD_TLS_AES_128_GCM_SHA256 )
6477+ ExpectIntEQ (test_tls13_AEAD_limit_triggers_KeyUpdate_cs (
6478+ "TLS13-AES128-GCM-SHA256" , 0 , 0x016A09E6 , wolfssl_aes_gcm ),
6479+ TEST_SUCCESS );
6480+ #endif
6481+ return EXPECT_RESULT ();
6482+ }
6483+
6484+ int test_tls13_AEAD_limit_KU_aes256_gcm_sha384 (void )
6485+ {
6486+ EXPECT_DECLS ;
6487+ #if defined(WOLFSSL_TLS13 ) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS ) && \
6488+ !defined(NO_WOLFSSL_CLIENT ) && !defined(NO_WOLFSSL_SERVER ) && \
6489+ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && \
6490+ defined(BUILD_TLS_AES_256_GCM_SHA384 )
6491+ ExpectIntEQ (test_tls13_AEAD_limit_triggers_KeyUpdate_cs (
6492+ "TLS13-AES256-GCM-SHA384" , 0 , 0x016A09E6 , wolfssl_aes_gcm ),
6493+ TEST_SUCCESS );
6494+ #endif
6495+ return EXPECT_RESULT ();
6496+ }
6497+
6498+ int test_tls13_AEAD_limit_KU_aes128_ccm_sha256 (void )
6499+ {
6500+ EXPECT_DECLS ;
6501+ #if defined(WOLFSSL_TLS13 ) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS ) && \
6502+ !defined(NO_WOLFSSL_CLIENT ) && !defined(NO_WOLFSSL_SERVER ) && \
6503+ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && \
6504+ defined(BUILD_TLS_AES_128_CCM_SHA256 )
6505+ ExpectIntEQ (test_tls13_AEAD_limit_triggers_KeyUpdate_cs (
6506+ "TLS13-AES128-CCM-SHA256" , 0 , 0x016A09E6 , wolfssl_aes_ccm ),
6507+ TEST_SUCCESS );
6508+ #endif
6509+ return EXPECT_RESULT ();
6510+ }
6511+
6512+ int test_tls13_AEAD_limit_KU_aes128_ccm_8_sha256 (void )
6513+ {
6514+ EXPECT_DECLS ;
6515+ #if defined(WOLFSSL_TLS13 ) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS ) && \
6516+ !defined(NO_WOLFSSL_CLIENT ) && !defined(NO_WOLFSSL_SERVER ) && \
6517+ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && \
6518+ defined(BUILD_TLS_AES_128_CCM_8_SHA256 )
6519+ ExpectIntEQ (test_tls13_AEAD_limit_triggers_KeyUpdate_cs (
6520+ "TLS13-AES128-CCM-8-SHA256" , 0 , 0x016A09E6 , wolfssl_aes_ccm ),
6521+ TEST_SUCCESS );
6522+ #endif
6523+ return EXPECT_RESULT ();
6524+ }
6525+
0 commit comments