@@ -675,6 +675,137 @@ static int test_dtls_communication(WOLFSSL *s, WOLFSSL *c)
675675 return EXPECT_RESULT ();
676676}
677677
678+ /* Drive a DTLS handshake until the dropping peer reads a genuine flight record,
679+ * corrupt its header, confirm the peer silently discards it, then re-deliver
680+ * the record and confirm the handshake still completes. */
681+ static int test_dtls_drop_invalid_record (method_provider method_c ,
682+ method_provider method_s , int dropServer , int unknownType )
683+ {
684+ EXPECT_DECLS ;
685+ WOLFSSL_CTX * ctx_c = NULL , * ctx_s = NULL ;
686+ WOLFSSL * ssl_c = NULL , * ssl_s = NULL ;
687+ struct test_memio_ctx test_ctx ;
688+ /* Sized for the small cookie-exchange/HRR/ClientHello records this test
689+ * corrupts; a PQC-default build with a large key_share may need more, and
690+ * the ExpectIntLE(recSz) guard below fails loudly rather than overflowing. */
691+ unsigned char rec [2048 ];
692+ unsigned char * recBuf ;
693+ int recSz = 0 ;
694+ int readIsClient = dropServer ? 0 : 1 ;
695+
696+ XMEMSET (& test_ctx , 0 , sizeof (test_ctx ));
697+ recBuf = readIsClient ? test_ctx .c_buff : test_ctx .s_buff ;
698+
699+ ExpectIntEQ (test_memio_setup (& test_ctx , & ctx_c , & ctx_s , & ssl_c , & ssl_s ,
700+ method_c , method_s ), 0 );
701+
702+ /* Client sends the ClientHello into the server's buffer. */
703+ wolfSSL_SetLoggingPrefix ("client" );
704+ ExpectIntEQ (wolfSSL_connect (ssl_c ), WOLFSSL_FATAL_ERROR );
705+ ExpectIntEQ (wolfSSL_get_error (ssl_c , WOLFSSL_FATAL_ERROR ),
706+ WOLFSSL_ERROR_WANT_READ );
707+
708+ /* When dropping at the client, let the server produce its first flight into
709+ * the client's buffer. */
710+ if (!dropServer ) {
711+ wolfSSL_SetLoggingPrefix ("server" );
712+ ExpectIntEQ (wolfSSL_accept (ssl_s ), WOLFSSL_FATAL_ERROR );
713+ ExpectIntEQ (wolfSSL_get_error (ssl_s , WOLFSSL_FATAL_ERROR ),
714+ WOLFSSL_ERROR_WANT_READ );
715+ }
716+
717+ /* Corrupt then replay the datagram the dropping peer is about to read. This
718+ * assumes a single-datagram flight (asserted below) so re-injecting cannot
719+ * merge record boundaries or drop other queued datagrams. */
720+ ExpectIntEQ (readIsClient ? test_ctx .c_msg_count : test_ctx .s_msg_count , 1 );
721+ recSz = readIsClient ? test_ctx .c_len : test_ctx .s_len ;
722+ ExpectIntGT (recSz , DTLS_RECORD_HEADER_SZ );
723+ ExpectIntLE (recSz , (int )sizeof (rec ));
724+ if (EXPECT_SUCCESS ()) {
725+ XMEMCPY (rec , recBuf , (size_t )recSz );
726+ if (unknownType ) {
727+ /* Not a valid ContentType, and not a DTLS 1.3 unified-header byte
728+ * (those have top bits 001, i.e. 0x20-0x3F). */
729+ recBuf [0 ] = 0x63 ;
730+ }
731+ else {
732+ recBuf [DTLS_RECORD_HEADER_SZ - 2 ] = 0xff ;
733+ recBuf [DTLS_RECORD_HEADER_SZ - 1 ] = 0xff ;
734+ }
735+ }
736+
737+ /* The dropping peer must silently discard the datagram: no fatal error and
738+ * nothing sent back to the other peer. */
739+ if (dropServer ) {
740+ wolfSSL_SetLoggingPrefix ("server" );
741+ ExpectIntEQ (wolfSSL_accept (ssl_s ), WOLFSSL_FATAL_ERROR );
742+ ExpectIntEQ (wolfSSL_get_error (ssl_s , WOLFSSL_FATAL_ERROR ),
743+ WOLFSSL_ERROR_WANT_READ );
744+ ExpectIntEQ (test_ctx .c_len , 0 );
745+ }
746+ else {
747+ wolfSSL_SetLoggingPrefix ("client" );
748+ ExpectIntEQ (wolfSSL_connect (ssl_c ), WOLFSSL_FATAL_ERROR );
749+ ExpectIntEQ (wolfSSL_get_error (ssl_c , WOLFSSL_FATAL_ERROR ),
750+ WOLFSSL_ERROR_WANT_READ );
751+ ExpectIntEQ (test_ctx .s_len , 0 );
752+ }
753+
754+ /* Re-deliver the genuine record and finish the handshake to prove the
755+ * association was preserved. */
756+ test_memio_clear_buffer (& test_ctx , readIsClient );
757+ ExpectIntEQ (test_memio_inject_message (& test_ctx , readIsClient ,
758+ (const char * )rec , recSz ),
759+ 0 );
760+ ExpectIntEQ (test_memio_do_handshake (ssl_c , ssl_s , 10 , NULL ), 0 );
761+
762+ ExpectIntEQ (test_dtls_communication (ssl_s , ssl_c ), TEST_SUCCESS );
763+
764+ wolfSSL_SetLoggingPrefix (NULL );
765+ wolfSSL_free (ssl_c );
766+ wolfSSL_free (ssl_s );
767+ wolfSSL_CTX_free (ctx_c );
768+ wolfSSL_CTX_free (ctx_s );
769+
770+ return EXPECT_RESULT ();
771+ }
772+
773+ /* A DTLS peer must silently discard a handshake record whose header fails
774+ * validation (UNKNOWN_RECORD_TYPE, LENGTH_ERROR) and still finish the handshake.
775+ * Cover client and server paths for DTLS 1.2 and 1.3. */
776+ int test_dtls_drop_invalid_record_during_handshake (void )
777+ {
778+ EXPECT_DECLS ;
779+
780+ /* Client drops a corrupted server flight: unknown type, then over-length. */
781+ ExpectIntEQ (test_dtls_drop_invalid_record (wolfDTLSv1_2_client_method ,
782+ wolfDTLSv1_2_server_method , 0 , 1 ), TEST_SUCCESS );
783+ ExpectIntEQ (test_dtls_drop_invalid_record (wolfDTLSv1_2_client_method ,
784+ wolfDTLSv1_2_server_method , 0 , 0 ), TEST_SUCCESS );
785+ /* Server drops a ClientHello with a bad record header: over-length, then
786+ * unknown ContentType (dropped by the new UNKNOWN_RECORD_TYPE clause, which
787+ * precedes the server-side non-stateful drop branch). */
788+ ExpectIntEQ (test_dtls_drop_invalid_record (wolfDTLSv1_2_client_method ,
789+ wolfDTLSv1_2_server_method , 1 , 0 ), TEST_SUCCESS );
790+ ExpectIntEQ (test_dtls_drop_invalid_record (wolfDTLSv1_2_client_method ,
791+ wolfDTLSv1_2_server_method , 1 , 1 ), TEST_SUCCESS );
792+
793+ #ifdef WOLFSSL_DTLS13
794+ /* Same silent-drop behavior on the DTLS 1.3 receive path (all four
795+ * side/corruption combinations). */
796+ ExpectIntEQ (test_dtls_drop_invalid_record (wolfDTLSv1_3_client_method ,
797+ wolfDTLSv1_3_server_method , 0 , 1 ), TEST_SUCCESS );
798+ ExpectIntEQ (test_dtls_drop_invalid_record (wolfDTLSv1_3_client_method ,
799+ wolfDTLSv1_3_server_method , 0 , 0 ), TEST_SUCCESS );
800+ ExpectIntEQ (test_dtls_drop_invalid_record (wolfDTLSv1_3_client_method ,
801+ wolfDTLSv1_3_server_method , 1 , 0 ), TEST_SUCCESS );
802+ ExpectIntEQ (test_dtls_drop_invalid_record (wolfDTLSv1_3_client_method ,
803+ wolfDTLSv1_3_server_method , 1 , 1 ), TEST_SUCCESS );
804+ #endif
805+
806+ return EXPECT_RESULT ();
807+ }
808+
678809#if defined(WOLFSSL_DTLS13 ) && !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS )
679810int test_dtls13_longer_length (void )
680811{
@@ -1007,6 +1138,10 @@ int test_dtls_short_ciphertext(void)
10071138 return EXPECT_RESULT ();
10081139}
10091140#else
1141+ int test_dtls_drop_invalid_record_during_handshake (void )
1142+ {
1143+ return TEST_SKIPPED ;
1144+ }
10101145int test_dtls_short_ciphertext (void )
10111146{
10121147 return TEST_SKIPPED ;
0 commit comments