@@ -6351,6 +6351,145 @@ int test_tls13_warning_alert_is_fatal(void)
63516351 return EXPECT_RESULT ();
63526352 }
63536353
6354+ /* Regression test for a malformed HelloRetryRequest sent to a downgrade capable
6355+ * (TLS 1.2 and TLS 1.3) client. Both crafted messages bear the HelloRetryRequest
6356+ * sentinel Random but omit supported_versions, which an HRR MUST carry, so the
6357+ * client must reject them with illegal_parameter (RFC 8446 Sec. 4.1.4/4.2)
6358+ * rather than downgrading to TLS 1.2 and losing the HRR context:
6359+ * 1. has-extensions form: carries a server_name extension (recognized but not
6360+ * permitted in an HRR). Before the fix the downgrade reparsed it as a plain
6361+ * server_hello, where server_name is allowed, and the client wrongly sent
6362+ * unsupported_extension (when SNI is compiled in). The fix rejects it in
6363+ * the version-negotiation step, so the assertions hold regardless of SNI.
6364+ * 2. no-extensions form: no extensions field at all. Before the fix this
6365+ * downgraded silently, treating the sentinel Random as a normal
6366+ * ServerHello.Random.
6367+ */
6368+ int test_tls13_hrr_recognized_ext_downgrade (void )
6369+ {
6370+ EXPECT_DECLS ;
6371+ #if defined(WOLFSSL_TLS13 ) && !defined(WOLFSSL_NO_TLS12 ) && \
6372+ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && \
6373+ !defined(NO_WOLFSSL_CLIENT ) && \
6374+ defined(WOLFSSL_AES_128 ) && defined(HAVE_AESGCM ) && !defined(NO_SHA256 ) && \
6375+ !defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT )
6376+ WOLFSSL_CTX * ctx_c = NULL ;
6377+ WOLFSSL * ssl_c = NULL ;
6378+ struct test_memio_ctx test_ctx ;
6379+ WOLFSSL_ALERT_HISTORY h ;
6380+ /* HelloRetryRequest bearing the sentinel Random and TLS_AES_128_GCM_SHA256,
6381+ * with NO supported_versions extension and a zero-length server_name
6382+ * extension (type 0x0000).
6383+ * extensions length: 4 (0x00,0x04)
6384+ * handshake body length: 44 (0x00,0x00,0x2c)
6385+ * record body length: 48 (0x00,0x30)
6386+ */
6387+ static const unsigned char hrr_sni_no_sv [] = {
6388+ /* TLS record header: handshake, TLS 1.2 compat, len=48 */
6389+ 0x16 , 0x03 , 0x03 , 0x00 , 0x30 ,
6390+ /* Handshake header: ServerHello, len=44 */
6391+ 0x02 , 0x00 , 0x00 , 0x2c ,
6392+ /* legacy_version: TLS 1.2 */
6393+ 0x03 , 0x03 ,
6394+ /* HelloRetryRequest magic random */
6395+ 0xcf , 0x21 , 0xad , 0x74 , 0xe5 , 0x9a , 0x61 , 0x11 ,
6396+ 0xbe , 0x1d , 0x8c , 0x02 , 0x1e , 0x65 , 0xb8 , 0x91 ,
6397+ 0xc2 , 0xa2 , 0x11 , 0x16 , 0x7a , 0xbb , 0x8c , 0x5e ,
6398+ 0x07 , 0x9e , 0x09 , 0xe2 , 0xc8 , 0xa8 , 0x33 , 0x9c ,
6399+ /* session ID length: 0 */
6400+ 0x00 ,
6401+ /* cipher suite: TLS_AES_128_GCM_SHA256 */
6402+ 0x13 , 0x01 ,
6403+ /* compression: null */
6404+ 0x00 ,
6405+ /* extensions length: 4 */
6406+ 0x00 , 0x04 ,
6407+ /* server_name extension type 0x0000, zero-length value */
6408+ 0x00 , 0x00 , 0x00 , 0x00
6409+ };
6410+ /* HelloRetryRequest bearing the sentinel Random with NO extensions field.
6411+ * handshake body length: 38 (0x00,0x00,0x26)
6412+ * record body length: 42 (0x00,0x2a)
6413+ */
6414+ static const unsigned char hrr_no_ext [] = {
6415+ /* TLS record header: handshake, TLS 1.2 compat, len=42 */
6416+ 0x16 , 0x03 , 0x03 , 0x00 , 0x2a ,
6417+ /* Handshake header: ServerHello, len=38 */
6418+ 0x02 , 0x00 , 0x00 , 0x26 ,
6419+ /* legacy_version: TLS 1.2 */
6420+ 0x03 , 0x03 ,
6421+ /* HelloRetryRequest magic random */
6422+ 0xcf , 0x21 , 0xad , 0x74 , 0xe5 , 0x9a , 0x61 , 0x11 ,
6423+ 0xbe , 0x1d , 0x8c , 0x02 , 0x1e , 0x65 , 0xb8 , 0x91 ,
6424+ 0xc2 , 0xa2 , 0x11 , 0x16 , 0x7a , 0xbb , 0x8c , 0x5e ,
6425+ 0x07 , 0x9e , 0x09 , 0xe2 , 0xc8 , 0xa8 , 0x33 , 0x9c ,
6426+ /* session ID length: 0 */
6427+ 0x00 ,
6428+ /* cipher suite: TLS_AES_128_GCM_SHA256 */
6429+ 0x13 , 0x01 ,
6430+ /* compression: null */
6431+ 0x00
6432+ };
6433+
6434+ /* Scenario 1: HelloRetryRequest with a not-permitted server_name extension
6435+ * and no supported_versions. */
6436+ XMEMSET (& test_ctx , 0 , sizeof (test_ctx ));
6437+ XMEMSET (& h , 0 , sizeof (h ));
6438+
6439+ /* Client allows both TLS 1.2 and TLS 1.3 so the missing supported_versions
6440+ * would otherwise trigger a downgrade. */
6441+ ExpectIntEQ (test_memio_setup (& test_ctx , & ctx_c , NULL , & ssl_c , NULL ,
6442+ wolfSSLv23_client_method , NULL ), 0 );
6443+
6444+ /* Inject the crafted HelloRetryRequest before the client starts the
6445+ * handshake. wolfSSL_connect sends the ClientHello then reads this. */
6446+ ExpectIntEQ (test_memio_inject_message (& test_ctx , 1 ,
6447+ (const char * )hrr_sni_no_sv , sizeof (hrr_sni_no_sv )), 0 );
6448+
6449+ /* Handshake must fail with EXT_NOT_ALLOWED (server_name is not permitted in
6450+ * a HelloRetryRequest), not UNSUPPORTED_EXTENSION. */
6451+ ExpectIntEQ (wolfSSL_connect (ssl_c ), -1 );
6452+ ExpectIntEQ (wolfSSL_get_error (ssl_c , -1 ),
6453+ WC_NO_ERR_TRACE (EXT_NOT_ALLOWED ));
6454+
6455+ /* RFC 8446 Sec. 4.2: the client MUST abort with illegal_parameter (47),
6456+ * not unsupported_extension (110). */
6457+ ExpectIntEQ (wolfSSL_get_alert_history (ssl_c , & h ), WOLFSSL_SUCCESS );
6458+ ExpectIntEQ (h .last_tx .code , illegal_parameter );
6459+ ExpectIntEQ (h .last_tx .level , alert_fatal );
6460+
6461+ wolfSSL_free (ssl_c );
6462+ ssl_c = NULL ;
6463+ wolfSSL_CTX_free (ctx_c );
6464+ ctx_c = NULL ;
6465+
6466+ /* Scenario 2: HelloRetryRequest sentinel with no extensions field at all.
6467+ * Must also be rejected with illegal_parameter rather than downgraded. */
6468+ XMEMSET (& test_ctx , 0 , sizeof (test_ctx ));
6469+ XMEMSET (& h , 0 , sizeof (h ));
6470+
6471+ ExpectIntEQ (test_memio_setup (& test_ctx , & ctx_c , NULL , & ssl_c , NULL ,
6472+ wolfSSLv23_client_method , NULL ), 0 );
6473+
6474+ ExpectIntEQ (test_memio_inject_message (& test_ctx , 1 ,
6475+ (const char * )hrr_no_ext , sizeof (hrr_no_ext )), 0 );
6476+
6477+ /* No offending extension is present here, so the client reports the message
6478+ * as an invalid HRR; the wire alert is still illegal_parameter. */
6479+ ExpectIntEQ (wolfSSL_connect (ssl_c ), -1 );
6480+ ExpectIntEQ (wolfSSL_get_error (ssl_c , -1 ),
6481+ WC_NO_ERR_TRACE (INVALID_PARAMETER ));
6482+
6483+ ExpectIntEQ (wolfSSL_get_alert_history (ssl_c , & h ), WOLFSSL_SUCCESS );
6484+ ExpectIntEQ (h .last_tx .code , illegal_parameter );
6485+ ExpectIntEQ (h .last_tx .level , alert_fatal );
6486+
6487+ wolfSSL_free (ssl_c );
6488+ wolfSSL_CTX_free (ctx_c );
6489+ #endif
6490+ return EXPECT_RESULT ();
6491+ }
6492+
63546493/* Test that wolfSSL_set1_sigalgs_list() is honored in TLS 1.3
63556494 */
63566495int test_tls13_cert_req_sigalgs (void )
0 commit comments