Commit 33cb1807a0 for openssl.org

commit 33cb1807a08bd8b723ceb054fcbd158e8d357347
Author: Matt Caswell <matt@openssl.foundation>
Date:   Thu Mar 19 11:44:48 2026 +0000

    Add a test for an early DTLS CCS with extra data on the last record

    We move the DTLS CCS early, and then add extra trailing data on the
    last record before the epoch change. We expect to see an unexpected
    message error.

    Reviewed-by: Frederik Wedel-Heinen <fwh.openssl@gmail.com>
    Reviewed-by: Paul Dale <paul.dale@oracle.com>
    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    MergeDate: Fri Apr  3 15:06:52 2026
    (Merged from https://github.com/openssl/openssl/pull/30503)

diff --git a/test/build.info b/test/build.info
index c7a9bd28f6..4789b5b596 100644
--- a/test/build.info
+++ b/test/build.info
@@ -54,7 +54,7 @@ IF[{- !$disabled{tests} -}]
           dtlsv1listentest ct_test threadstest d2i_test \
           ssl_test_ctx_test ssl_test x509aux cipherlist_test asynciotest \
           bio_callback_test bio_memleak_test bio_core_test bio_dgram_test param_build_test \
-          sslapitest ssl_handshake_rtt_test dtlstest dtls_ccs_reorder_test sslcorrupttest \
+          sslapitest ssl_handshake_rtt_test dtlstest sslcorrupttest \
           bio_base64_test bio_enc_test pkey_meth_kdf_test evp_kdf_test uitest \
           cipherbytes_test threadstest_fips threadpool_test \
           asn1_encode_test asn1_decode_test asn1_string_table_test asn1_stable_parse_test \
@@ -98,6 +98,10 @@ IF[{- !$disabled{tests} -}]
     PROGRAMS{noinst}=cert_comp_test
   ENDIF

+  IF[{- !$disabled{dtls} -}]
+    PROGRAMS{noinst}=dtls_ccs_reorder_test
+  ENDIF
+
   SOURCE[confdump]=confdump.c
   INCLUDE[confdump]=../include ../apps/include
   DEPEND[confdump]=../libcrypto
@@ -657,9 +661,11 @@ IF[{- !$disabled{tests} -}]
   INCLUDE[dtlstest]=../include ../apps/include
   DEPEND[dtlstest]=../libcrypto ../libssl libtestutil.a

-  SOURCE[dtls_ccs_reorder_test]=dtls_ccs_reorder_test.c helpers/ssltestlib.c
-  INCLUDE[dtls_ccs_reorder_test]=../include ../apps/include
-  DEPEND[dtls_ccs_reorder_test]=../libcrypto ../libssl libtestutil.a
+  IF[{- !$disabled{dtls} -}]
+    SOURCE[dtls_ccs_reorder_test]=dtls_ccs_reorder_test.c helpers/ssltestlib.c
+    INCLUDE[dtls_ccs_reorder_test]=../include ../apps/include
+    DEPEND[dtls_ccs_reorder_test]=../libcrypto ../libssl libtestutil.a
+  ENDIF

   SOURCE[sslcorrupttest]=sslcorrupttest.c helpers/ssltestlib.c
   INCLUDE[sslcorrupttest]=../include ../apps/include
diff --git a/test/dtls_ccs_reorder_test.c b/test/dtls_ccs_reorder_test.c
index 5e145c961d..5794c5e02f 100644
--- a/test/dtls_ccs_reorder_test.c
+++ b/test/dtls_ccs_reorder_test.c
@@ -22,8 +22,6 @@
 static char *cert = NULL;
 static char *privkey = NULL;

-#ifndef OPENSSL_NO_DTLS
-
 static unsigned int infinite_timer_cb(SSL *s, unsigned int timer_us)
 {
     (void)s;
@@ -462,7 +460,64 @@ end:
     return testresult;
 }

-#endif /* OPENSSL_NO_DTLS */
+static int test_dtls_data_after_ccs(void)
+{
+    SSL_CTX *sctx = NULL, *cctx = NULL;
+    SSL *sssl = NULL, *cssl = NULL;
+    BIO *bio;
+    int testresult = 0, ret;
+    int target_pkt, target_rec;
+
+    if (!TEST_true(create_ssl_ctx_pair(NULL, DTLS_server_method(),
+            DTLS_client_method(),
+            DTLS1_2_VERSION, DTLS1_2_VERSION,
+            &sctx, &cctx, cert, privkey)))
+        return 0;
+
+    if (!TEST_true(create_ssl_objects(sctx, cctx, &sssl, &cssl, NULL, NULL)))
+        goto end;
+
+    DTLS_set_timer_cb(sssl, infinite_timer_cb);
+    DTLS_set_timer_cb(cssl, infinite_timer_cb);
+
+    if (!TEST_int_le(SSL_connect(cssl), 0))
+        goto end;
+
+    if (!TEST_int_le(SSL_accept(sssl), 0))
+        goto end;
+
+    if (!TEST_int_le(SSL_connect(cssl), 0))
+        goto end;
+
+    bio = SSL_get_wbio(cssl);
+    if (!TEST_ptr(bio)
+        || !TEST_true(reorder_ccs(bio, SSL3_MT_CLIENT_KEY_EXCHANGE)))
+        goto end;
+
+    if (!TEST_true(mempacket_find_record(bio, SSL3_RT_HANDSHAKE,
+            SSL3_MT_CLIENT_KEY_EXCHANGE,
+            &target_pkt, &target_rec)))
+        goto end;
+    if (!TEST_true(mempacket_append_to_record(bio, target_pkt, target_rec,
+            (unsigned char *)"test data", 9)))
+        goto end;
+
+    ret = SSL_accept(sssl);
+    if (!TEST_int_le(ret, 0))
+        goto end;
+    if (!TEST_int_eq(SSL_get_error(sssl, ret), SSL_ERROR_SSL))
+        goto end;
+    if (!TEST_int_eq(ERR_GET_REASON(ERR_get_error()), SSL_R_UNEXPECTED_MESSAGE))
+        goto end;
+
+    testresult = 1;
+end:
+    SSL_free(sssl);
+    SSL_free(cssl);
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+    return testresult;
+}

 int setup_tests(void)
 {
@@ -475,11 +530,10 @@ int setup_tests(void)
         || !TEST_ptr(privkey = test_get_argument(1)))
         return 0;

-#ifndef OPENSSL_NO_DTLS
     ADD_ALL_TESTS(test_dtls_ccs_full_hs, OSSL_NELEM(full_hs_tests));
     ADD_ALL_TESTS(test_dtls_ccs_before_nst, OSSL_NELEM(nst_versions));
     ADD_ALL_TESTS(test_dtls_ccs_resume, OSSL_NELEM(resume_tests));
-#endif
+    ADD_TEST(test_dtls_data_after_ccs);

     return 1;
 }
diff --git a/test/helpers/ssltestlib.c b/test/helpers/ssltestlib.c
index 8e512eaabb..57b8c3e040 100644
--- a/test/helpers/ssltestlib.c
+++ b/test/helpers/ssltestlib.c
@@ -636,6 +636,59 @@ int mempacket_split_packet_at(BIO *bio, int pktidx, int recidx)
     return 1;
 }

+/*
+ * Append arbitrary data to a given record. The record must be the last record
+ * in the packet
+ */
+int mempacket_append_to_record(BIO *bio, int pktidx, int recidx,
+    unsigned char *data, size_t datalen)
+{
+    MEMPACKET_TEST_CTX *ctx = BIO_get_data(bio);
+    MEMPACKET *srcpkt;
+    size_t rem, len;
+    int i;
+    unsigned char *rec, *tmp;
+
+    if (ctx == NULL)
+        return 0;
+    srcpkt = sk_MEMPACKET_value(ctx->pkts, pktidx);
+    if (srcpkt == NULL)
+        return 0;
+
+    tmp = OPENSSL_realloc(srcpkt->data, srcpkt->len + datalen);
+    if (tmp == NULL)
+        return 0;
+    srcpkt->data = tmp;
+
+    /* Parse the records in the packet looking for the target record index */
+    for (i = 0, rem = srcpkt->len, rec = srcpkt->data;
+        rem >= DTLS1_RT_HEADER_LENGTH && i < recidx;
+        i++, rem -= len, rec += len) {
+        len = ((rec[RECORD_LEN_HI] << 8) | rec[RECORD_LEN_LO])
+            + DTLS1_RT_HEADER_LENGTH;
+        if (rem < len)
+            return 0;
+    }
+
+    if (i != recidx || rem < DTLS1_RT_HEADER_LENGTH)
+        return 0;
+
+    len = (rec[RECORD_LEN_HI] << 8) | rec[RECORD_LEN_LO];
+    /* We can only append to the last record */
+    if (rem != len + DTLS1_RT_HEADER_LENGTH)
+        return 0;
+
+    /* Check we can fit the extra data in the record */
+    if (0xffff - len < datalen)
+        return 0;
+    len += datalen;
+    rec[RECORD_LEN_HI] = (len >> 8) & 0xff;
+    rec[RECORD_LEN_LO] = len & 0xff;
+    memcpy(srcpkt->data + srcpkt->len, data, datalen);
+    srcpkt->len += (int)datalen;
+    return 1;
+}
+
 int mempacket_dup_last_packet(BIO *bio)
 {
     MEMPACKET_TEST_CTX *ctx = BIO_get_data(bio);
diff --git a/test/helpers/ssltestlib.h b/test/helpers/ssltestlib.h
index 54aeb6b50c..68a10b24bd 100644
--- a/test/helpers/ssltestlib.h
+++ b/test/helpers/ssltestlib.h
@@ -77,6 +77,8 @@ int mempacket_move_packet(BIO *bio, int d, int s);
 int mempacket_find_record(BIO *bio, int rectype, int hs_msg_type,
     int *pktidx, int *recidx);
 int mempacket_split_packet_at(BIO *bio, int pktidx, int recidx);
+int mempacket_append_to_record(BIO *bio, int pktidx, int recidx,
+    unsigned char *data, size_t datalen);
 int mempacket_dup_last_packet(BIO *bio);
 int mempacket_test_inject(BIO *bio, const char *in, int inl, int pktnum,
     int type);