@@ -6189,3 +6189,216 @@ int test_tls13_cipher_fuzz_aes128_ccm_8_sha256(void)
61896189#endif
61906190 return EXPECT_RESULT ();
61916191}
6192+
6193+ /* Regression test for the AEAD record-protection limit constants in
6194+ * internal.h. The macros expand to w64From32(hi, lo). A prior version split
6195+ * the intended 32-bit constants into 16-bit halves and passed each half as
6196+ * a separate 32-bit argument, producing a 64-bit value many orders of
6197+ * magnitude larger than RFC 8446 / RFC 9147 require. That made
6198+ * CheckTLS13AEADSendLimit's key-update trigger effectively unreachable.
6199+ * Compare against the hard-coded spec values so a recurrence is caught even
6200+ * if the macro is reused on both sides of the comparison. */
6201+ int test_tls13_AEAD_limit_macros (void )
6202+ {
6203+ EXPECT_DECLS ;
6204+ #if defined(WOLFSSL_TLS13 ) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS )
6205+ w64wrapper limit ;
6206+
6207+ /* RFC 8446 5.5: 2^24.5 ~= 23726566 (0x016A09E6). */
6208+ limit = AEAD_AES_LIMIT ;
6209+ ExpectIntEQ (w64GetHigh32 (limit ), 0 );
6210+ ExpectIntEQ (w64GetLow32 (limit ), 0x016A09E6 );
6211+
6212+ #ifdef WOLFSSL_DTLS13
6213+ /* RFC 9147 (AES-CCM integrity): 2^23.5 ~= 11863283 (0x00B504F3). */
6214+ limit = DTLS_AEAD_AES_CCM_FAIL_LIMIT ;
6215+ ExpectIntEQ (w64GetHigh32 (limit ), 0 );
6216+ ExpectIntEQ (w64GetLow32 (limit ), 0x00B504F3 );
6217+
6218+ /* Key-update threshold is half the fail limit: 5931641 (0x005A8279). */
6219+ limit = DTLS_AEAD_AES_CCM_FAIL_KU_LIMIT ;
6220+ ExpectIntEQ (w64GetHigh32 (limit ), 0 );
6221+ ExpectIntEQ (w64GetLow32 (limit ), 0x005A8279 );
6222+ #endif
6223+ #endif
6224+ return EXPECT_RESULT ();
6225+ }
6226+
6227+ #if defined(WOLFSSL_TLS13 ) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS ) && \
6228+ !defined(NO_WOLFSSL_CLIENT ) && !defined(NO_WOLFSSL_SERVER ) && \
6229+ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && \
6230+ (defined(BUILD_TLS_AES_128_GCM_SHA256 ) || \
6231+ defined(BUILD_TLS_AES_256_GCM_SHA384 ) || \
6232+ defined(BUILD_TLS_AES_128_CCM_SHA256 ) || \
6233+ defined(BUILD_TLS_AES_128_CCM_8_SHA256 ))
6234+ /* Drive the client's encrypt sequence number towards the spec limit for
6235+ * `suite` and verify CheckTLS13AEADSendLimit's KeyUpdate trigger fires at
6236+ * exactly the right boundary.
6237+ *
6238+ * Two writes are exercised:
6239+ * 1. Counter set to limit - 2. After the write the counter must read
6240+ * limit - 1 (record incremented it by 1) and no KeyUpdate must have
6241+ * been emitted. CheckTLS13AEADSendLimit uses `seq >= limit`, so neither
6242+ * the pre-send check nor the trailing loop check (which runs once more
6243+ * after the last record before wolfSSL_write exits) is allowed to fire.
6244+ * 2. A second write follows with the counter already sitting at limit - 1
6245+ * from the previous record. The user record goes out at seq = limit-1,
6246+ * which bumps the counter to limit; the trailing limit check then
6247+ * fires SendTls13KeyUpdate. SetKeysSide zeroes the encrypt counter, so
6248+ * the post-write counter is 0.
6249+ *
6250+ * With the previous broken AEAD-limit macros the limit was unreachable, no
6251+ * KeyUpdate would ever fire, and the counter would simply advance to
6252+ * limit_lo + 1 in the second case instead of being reset.
6253+ *
6254+ * The AEAD nonce mixes in the record sequence number on both sides, so the
6255+ * server's decrypt counter has to be advanced in lockstep with the client's
6256+ * encrypt counter or the record fails the integrity check. */
6257+ static int test_tls13_AEAD_limit_triggers_KeyUpdate_cs (const char * suite ,
6258+ word32 limit_hi , word32 limit_lo , int expected_bulk_cipher )
6259+ {
6260+ EXPECT_DECLS ;
6261+ struct test_memio_ctx test_ctx ;
6262+ WOLFSSL_CTX * ctx_c = NULL , * ctx_s = NULL ;
6263+ WOLFSSL * ssl_c = NULL , * ssl_s = NULL ;
6264+ const char msg [] = "post-limit-record" ;
6265+ char buf [sizeof (msg )];
6266+ int written ;
6267+
6268+ XMEMSET (& test_ctx , 0 , sizeof (test_ctx ));
6269+ test_ctx .c_ciphers = suite ;
6270+ test_ctx .s_ciphers = suite ;
6271+
6272+ ExpectIntEQ (test_memio_setup (& test_ctx , & ctx_c , & ctx_s , & ssl_c , & ssl_s ,
6273+ wolfTLSv1_3_client_method , wolfTLSv1_3_server_method ), 0 );
6274+ ExpectIntEQ (test_memio_do_handshake (ssl_c , ssl_s , 10 , NULL ), 0 );
6275+
6276+ if (EXPECT_SUCCESS () && ssl_c != NULL && ssl_s != NULL ) {
6277+ /* Sanity check: the negotiated bulk cipher matches what the caller
6278+ * intends to exercise. If a build flag combination falls through to
6279+ * a different suite, the limit constant would be wrong. */
6280+ ExpectIntEQ (ssl_c -> specs .bulk_cipher_algorithm , expected_bulk_cipher );
6281+
6282+ /* Stage the counters two below the limit so the first write stays
6283+ * comfortably below the trigger threshold. */
6284+ ssl_c -> keys .sequence_number_hi = limit_hi ;
6285+ ssl_c -> keys .sequence_number_lo = limit_lo - 2 ;
6286+ ssl_s -> keys .peer_sequence_number_hi = limit_hi ;
6287+ ssl_s -> keys .peer_sequence_number_lo = limit_lo - 2 ;
6288+ }
6289+
6290+ /* First write: below the limit, no KeyUpdate expected. */
6291+ written = wolfSSL_write (ssl_c , msg , (int )sizeof (msg ));
6292+ ExpectIntEQ (written , (int )sizeof (msg ));
6293+
6294+ if (EXPECT_SUCCESS () && ssl_c != NULL ) {
6295+ /* The record bumped the counter from limit-2 to limit-1. A
6296+ * KeyUpdate would have zeroed it via SetKeysSide and bumped to 1. */
6297+ ExpectIntEQ ((int )ssl_c -> keys .sequence_number_hi , (int )limit_hi );
6298+ ExpectIntEQ (ssl_c -> keys .sequence_number_lo , limit_lo - 1 );
6299+ }
6300+
6301+ /* Server consumes the below-limit record with its existing keys. */
6302+ XMEMSET (buf , 0 , sizeof (buf ));
6303+ ExpectIntEQ (wolfSSL_read (ssl_s , buf , (int )sizeof (buf )), (int )sizeof (msg ));
6304+ ExpectIntEQ (XMEMCMP (buf , msg , sizeof (msg )), 0 );
6305+
6306+ /* Second write: the client's counter is now at limit-1. Sending this
6307+ * record will push it to limit, at which point the trailing check
6308+ * inside SendData's loop fires SendTls13KeyUpdate. No manual counter
6309+ * adjustment is needed -- the counter is allowed to "naturally" reach
6310+ * the limit through the previous send. */
6311+ written = wolfSSL_write (ssl_c , msg , (int )sizeof (msg ));
6312+ ExpectIntEQ (written , (int )sizeof (msg ));
6313+
6314+ if (EXPECT_SUCCESS () && ssl_c != NULL ) {
6315+ /* SendTls13KeyUpdate -> DeriveTls13Keys -> SetKeysSide zeroes the
6316+ * encrypt sequence number. The user record went out before the
6317+ * trigger fired, so no record was sent on the new keys. */
6318+ ExpectIntEQ ((int )ssl_c -> keys .sequence_number_hi , 0 );
6319+ ExpectIntEQ ((int )ssl_c -> keys .sequence_number_lo , 0 );
6320+ }
6321+
6322+ /* The server reads the user record (sent under the pre-update keys at
6323+ * seq = limit - 1) before it sees the KeyUpdate record. The KeyUpdate
6324+ * is consumed transparently on a subsequent read; for the test we just
6325+ * need to confirm the user data round-trips. */
6326+ XMEMSET (buf , 0 , sizeof (buf ));
6327+ {
6328+ int r = -1 , attempts ;
6329+ for (attempts = 0 ; attempts < 5 ; attempts ++ ) {
6330+ r = wolfSSL_read (ssl_s , buf , (int )sizeof (buf ));
6331+ if (r > 0 )
6332+ break ;
6333+ if (wolfSSL_get_error (ssl_s , r ) != WOLFSSL_ERROR_WANT_READ )
6334+ break ;
6335+ }
6336+ ExpectIntEQ (r , (int )sizeof (msg ));
6337+ }
6338+ ExpectIntEQ (XMEMCMP (buf , msg , sizeof (msg )), 0 );
6339+
6340+ wolfSSL_free (ssl_c );
6341+ wolfSSL_free (ssl_s );
6342+ wolfSSL_CTX_free (ctx_c );
6343+ wolfSSL_CTX_free (ctx_s );
6344+
6345+ return EXPECT_RESULT ();
6346+ }
6347+ #endif
6348+
6349+ int test_tls13_AEAD_limit_KU_aes128_gcm_sha256 (void )
6350+ {
6351+ EXPECT_DECLS ;
6352+ #if defined(WOLFSSL_TLS13 ) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS ) && \
6353+ !defined(NO_WOLFSSL_CLIENT ) && !defined(NO_WOLFSSL_SERVER ) && \
6354+ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && \
6355+ defined(BUILD_TLS_AES_128_GCM_SHA256 )
6356+ ExpectIntEQ (test_tls13_AEAD_limit_triggers_KeyUpdate_cs (
6357+ "TLS13-AES128-GCM-SHA256" , 0 , 0x016A09E6 , wolfssl_aes_gcm ),
6358+ TEST_SUCCESS );
6359+ #endif
6360+ return EXPECT_RESULT ();
6361+ }
6362+
6363+ int test_tls13_AEAD_limit_KU_aes256_gcm_sha384 (void )
6364+ {
6365+ EXPECT_DECLS ;
6366+ #if defined(WOLFSSL_TLS13 ) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS ) && \
6367+ !defined(NO_WOLFSSL_CLIENT ) && !defined(NO_WOLFSSL_SERVER ) && \
6368+ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && \
6369+ defined(BUILD_TLS_AES_256_GCM_SHA384 )
6370+ ExpectIntEQ (test_tls13_AEAD_limit_triggers_KeyUpdate_cs (
6371+ "TLS13-AES256-GCM-SHA384" , 0 , 0x016A09E6 , wolfssl_aes_gcm ),
6372+ TEST_SUCCESS );
6373+ #endif
6374+ return EXPECT_RESULT ();
6375+ }
6376+
6377+ int test_tls13_AEAD_limit_KU_aes128_ccm_sha256 (void )
6378+ {
6379+ EXPECT_DECLS ;
6380+ #if defined(WOLFSSL_TLS13 ) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS ) && \
6381+ !defined(NO_WOLFSSL_CLIENT ) && !defined(NO_WOLFSSL_SERVER ) && \
6382+ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && \
6383+ defined(BUILD_TLS_AES_128_CCM_SHA256 )
6384+ ExpectIntEQ (test_tls13_AEAD_limit_triggers_KeyUpdate_cs (
6385+ "TLS13-AES128-CCM-SHA256" , 0 , 0x016A09E6 , wolfssl_aes_ccm ),
6386+ TEST_SUCCESS );
6387+ #endif
6388+ return EXPECT_RESULT ();
6389+ }
6390+
6391+ int test_tls13_AEAD_limit_KU_aes128_ccm_8_sha256 (void )
6392+ {
6393+ EXPECT_DECLS ;
6394+ #if defined(WOLFSSL_TLS13 ) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS ) && \
6395+ !defined(NO_WOLFSSL_CLIENT ) && !defined(NO_WOLFSSL_SERVER ) && \
6396+ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && \
6397+ defined(BUILD_TLS_AES_128_CCM_8_SHA256 )
6398+ ExpectIntEQ (test_tls13_AEAD_limit_triggers_KeyUpdate_cs (
6399+ "TLS13-AES128-CCM-8-SHA256" , 0 , 0x016A09E6 , wolfssl_aes_ccm ),
6400+ TEST_SUCCESS );
6401+ #endif
6402+ return EXPECT_RESULT ();
6403+ }
6404+
0 commit comments