Modify test/quic_record_test.c to also depacketize
authorRichard Levitte <levitte@openssl.org>
Tue, 6 Sep 2022 12:08:15 +0000 (14:08 +0200)
committerRichard Levitte <levitte@openssl.org>
Fri, 23 Sep 2022 15:59:04 +0000 (17:59 +0200)
Reviewed-by: Hugo Landau <hlandau@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/18838)

test/quic_record_test.c

index a4cafb8c21ae641f8685b8ea4da1ced478aac0ca..87651773da74c533a7c67ac7a797065bf3018aff 100644 (file)
@@ -8,7 +8,11 @@
  */
 
 #include "internal/quic_record_rx.h"
+#include "internal/quic_rx_depack.h"
 #include "internal/quic_record_tx.h"
+#include "internal/quic_ackm.h"
+#include "internal/quic_cc.h"
+#include "internal/quic_ssl.h"
 #include "testutil.h"
 
 static const QUIC_CONN_ID empty_conn_id = {0, {0}};
@@ -27,8 +31,13 @@ static const QUIC_CONN_ID empty_conn_id = {0, {0}};
 #define RX_TEST_OP_KEY_UPDATE_TIMEOUT     11 /* complete key update process */
 #define RX_TEST_OP_SET_INIT_KEY_PHASE     12 /* initial Key Phase bit value */
 
+/* These are subtest ops for RX_TEST_OP_CHECK_PKT, to additionally check frames */
+#define RX_TEST_OP_CHECK_PKT_FRAMES_OK        1 /* check that frames are parsed ok */
+#define RX_TEST_OP_CHECK_PKT_FRAMES_INVALID   2 /* check that frames fail to parse ok */
+
 struct rx_test_op {
     unsigned char op;
+    unsigned char subop;
     const unsigned char *buf;
     size_t buf_len;
     const QUIC_PKT_HDR *hdr;
@@ -36,50 +45,76 @@ struct rx_test_op {
     QUIC_PN largest_pn;
     const QUIC_CONN_ID *dcid;
     int (*new_qrx)(QUIC_DEMUX **demux, OSSL_QRX **qrx);
+
+    /* For frame checking */
 };
 
 #define RX_OP_END \
     { RX_TEST_OP_END }
 #define RX_OP_SET_SCID_LEN(scid_len) \
-    { RX_TEST_OP_SET_SCID_LEN, NULL, 0, NULL, (scid_len), 0, 0, NULL, NULL },
+    { RX_TEST_OP_SET_SCID_LEN, 0, NULL, 0, NULL, (scid_len), 0, 0, NULL, NULL },
 #define RX_OP_SET_INIT_LARGEST_PN(largest_pn) \
-    { RX_TEST_OP_SET_INIT_LARGEST_PN, NULL, 0, NULL, 0, 0, (largest_pn), NULL, NULL },
+    { RX_TEST_OP_SET_INIT_LARGEST_PN, 0, NULL, 0, NULL, 0, 0, (largest_pn), NULL, NULL },
 #define RX_OP_ADD_RX_DCID(dcid) \
-    { RX_TEST_OP_ADD_RX_DCID, NULL, 0, NULL, 0, 0, 0, &(dcid), NULL },
+    { RX_TEST_OP_ADD_RX_DCID, 0, NULL, 0, NULL, 0, 0, 0, &(dcid), NULL },
 #define RX_OP_INJECT(dgram) \
-    { RX_TEST_OP_INJECT, (dgram), sizeof(dgram), NULL, 0, 0, 0, NULL },
+    { RX_TEST_OP_INJECT, 0, (dgram), sizeof(dgram), NULL, 0, 0, 0, NULL },
 #define RX_OP_PROVIDE_SECRET(el, suite, key)                           \
     {                                                               \
-        RX_TEST_OP_PROVIDE_SECRET, (key), sizeof(key),                 \
+        RX_TEST_OP_PROVIDE_SECRET, 0, (key), sizeof(key),             \
         NULL, (el), (suite), 0, NULL, NULL                          \
     },
 #define RX_OP_PROVIDE_SECRET_INITIAL(dcid) \
-    { RX_TEST_OP_PROVIDE_SECRET_INITIAL, NULL, 0, NULL, 0, 0, 0, &(dcid), NULL },
+    { RX_TEST_OP_PROVIDE_SECRET_INITIAL, 0, NULL, 0, NULL, 0, 0, 0, &(dcid), NULL },
 #define RX_OP_DISCARD_EL(el) \
-    { RX_TEST_OP_DISCARD_EL, NULL, 0, NULL, (el), 0, 0, NULL, NULL },
+    { RX_TEST_OP_DISCARD_EL, 0, NULL, 0, NULL, (el), 0, 0, NULL, NULL },
 #define RX_OP_CHECK_PKT(expect_hdr, expect_body)                       \
     {                                                               \
-        RX_TEST_OP_CHECK_PKT, (expect_body), sizeof(expect_body),      \
+        RX_TEST_OP_CHECK_PKT, 0, (expect_body), sizeof(expect_body),  \
         &(expect_hdr), 0, 0, 0, NULL, NULL                          \
     },
+#define RX_OP_CHECK_PKT_FRAMES_OK(expect_hdr, expect_body)          \
+    {                                                               \
+        RX_TEST_OP_CHECK_PKT, RX_TEST_OP_CHECK_PKT_FRAMES_OK,       \
+        (expect_body), sizeof(expect_body), &(expect_hdr),          \
+        0, 0, 0, NULL, NULL                                         \
+    },
+#define RX_OP_CHECK_PKT_FRAMES_INVALID(expect_hdr, expect_body)     \
+    {                                                               \
+        RX_TEST_OP_CHECK_PKT, RX_TEST_OP_CHECK_PKT_FRAMES_INVALID,  \
+        (expect_body), sizeof(expect_body), &(expect_hdr),          \
+        0, 0, 0, NULL, NULL                                         \
+    },
 #define RX_OP_CHECK_NO_PKT() \
-    { RX_TEST_OP_CHECK_NO_PKT, NULL, 0, NULL, 0, 0, 0, NULL, NULL },
+    { RX_TEST_OP_CHECK_NO_PKT, 0, NULL, 0, NULL, 0, 0, 0, NULL, NULL },
 #define RX_OP_CHECK_KEY_EPOCH(expected) \
-    { RX_TEST_OP_CHECK_KEY_EPOCH, NULL, 0, NULL, 0, 0, (expected), NULL },
+    { RX_TEST_OP_CHECK_KEY_EPOCH, 0, NULL, 0, NULL, 0, 0, (expected), NULL },
 #define RX_OP_KEY_UPDATE_TIMEOUT(normal) \
-    { RX_TEST_OP_KEY_UPDATE_TIMEOUT, NULL, 0, NULL, (normal), 0, 0, NULL },
+    { RX_TEST_OP_KEY_UPDATE_TIMEOUT, 0, NULL, 0, NULL, (normal), 0, 0, NULL },
 #define RX_OP_SET_INIT_KEY_PHASE(kp_bit) \
-    { RX_TEST_OP_SET_INIT_KEY_PHASE, NULL, 0, NULL, (kp_bit), 0, 0, NULL },
+    { RX_TEST_OP_SET_INIT_KEY_PHASE, 0, NULL, 0, NULL, (kp_bit), 0, 0, NULL },
 
 #define RX_OP_INJECT_N(n)                                          \
     RX_OP_INJECT(rx_script_##n##_in)
 #define RX_OP_CHECK_PKT_N(n)                                       \
     RX_OP_CHECK_PKT(rx_script_##n##_expect_hdr, rx_script_##n##_body)
+#define RX_OP_CHECK_PKT_FRAMES_OK_N(n)                             \
+    RX_OP_CHECK_PKT_FRAMES_OK(rx_script_##n##_expect_hdr, rx_script_##n##_body)
+#define RX_OP_CHECK_PKT_FRAMES_INVALID_N(n)                        \
+    RX_OP_CHECK_PKT_FRAMES_INVALID(rx_script_##n##_expect_hdr, rx_script_##n##_body)
 
-#define RX_OP_INJECT_CHECK(n)                                      \
+#define RX_OP_INJECT_CHECK(n)                                  \
     RX_OP_INJECT_N(n)                                              \
     RX_OP_CHECK_PKT_N(n)
 
+#define RX_OP_INJECT_CHECK_FRAMES_OK(n)                            \
+    RX_OP_INJECT_N(n)                                              \
+    RX_OP_CHECK_PKT_FRAMES_OK_N(n)
+
+#define RX_OP_INJECT_CHECK_FRAMES_INVALID(n)                       \
+    RX_OP_INJECT_N(n)                                              \
+    RX_OP_CHECK_PKT_FRAMES_INVALID_N(3)
+
 /* 1. RFC 9001 - A.3 Server Initial */
 static const unsigned char rx_script_1_in[] = {
     0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a,
@@ -103,7 +138,7 @@ static const unsigned char rx_script_1_body[] = {
     0x25, 0xdf, 0x56, 0x6d, 0xc5, 0x43, 0x0b, 0x9a, 0x04, 0x5a, 0x12, 0x00,
     0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24, 0x00, 0x1d, 0x00,
     0x20, 0x9d, 0x3c, 0x94, 0x0d, 0x89, 0x69, 0x0b, 0x84, 0xd0, 0x8a, 0x60,
-    0x99, 0x3c, 0x14, 0x4e, 0xca, 0x68, 0x4d, 0x10,  0x81, 0x28, 0x7c, 0x83,
+    0x99, 0x3c, 0x14, 0x4e, 0xca, 0x68, 0x4d, 0x10, 0x81, 0x28, 0x7c, 0x83,
     0x4d, 0x53, 0x11, 0xbc, 0xf3, 0x2b, 0xb9, 0xda, 0x1a, 0x00, 0x2b, 0x00,
     0x02, 0x03, 0x04
 };
@@ -126,7 +161,7 @@ static const struct rx_test_op rx_script_1[] = {
     RX_OP_SET_INIT_LARGEST_PN(0)
     RX_OP_ADD_RX_DCID(empty_conn_id)
     RX_OP_PROVIDE_SECRET_INITIAL(rx_script_1_dcid)
-    RX_OP_INJECT_CHECK(1)
+    RX_OP_INJECT_CHECK_FRAMES_OK(1)
     RX_OP_CHECK_NO_PKT()
     RX_OP_END
 };
@@ -160,7 +195,7 @@ static const struct rx_test_op rx_script_2[] = {
     RX_OP_ADD_RX_DCID(empty_conn_id)
     RX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT, QRL_SUITE_CHACHA20POLY1305,
                          rx_script_2_secret)
-    RX_OP_INJECT_CHECK(2)
+    RX_OP_INJECT_CHECK_FRAMES_OK(2)
     RX_OP_CHECK_NO_PKT()
     RX_OP_END
 };
@@ -200,7 +235,12 @@ static const unsigned char rx_script_3_body[] = {
 
 static const struct rx_test_op rx_script_3[] = {
     RX_OP_ADD_RX_DCID(empty_conn_id)
-    RX_OP_INJECT_CHECK(3)
+    /*
+     * This is a version negotiation packet, so doesn't have any frames.
+     * However, the depacketizer still handles this sort of packet, so
+     * we still pass the packet to it, to exercise what it does.
+     */
+    RX_OP_INJECT_CHECK_FRAMES_OK(3)
     RX_OP_CHECK_NO_PKT()
     RX_OP_END
 };
@@ -254,7 +294,7 @@ static const unsigned char rx_script_4_body[] = {
 
 static const struct rx_test_op rx_script_4[] = {
     RX_OP_ADD_RX_DCID(empty_conn_id)
-    RX_OP_INJECT_CHECK(4)
+    RX_OP_INJECT_CHECK_FRAMES_OK(4)
     RX_OP_CHECK_NO_PKT()
     RX_OP_END
 };
@@ -562,15 +602,15 @@ static const struct rx_test_op rx_script_5[] = {
     RX_OP_ADD_RX_DCID(empty_conn_id)
     RX_OP_PROVIDE_SECRET_INITIAL(rx_script_5_c2s_init_dcid)
     RX_OP_INJECT_N(5)
-    RX_OP_CHECK_PKT_N(5a)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(5a)
     RX_OP_CHECK_NO_PKT() /* not got secret for next packet yet */
     RX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_HANDSHAKE,
                       QRL_SUITE_AES128GCM, rx_script_5_handshake_secret)
-    RX_OP_CHECK_PKT_N(5b)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(5b)
     RX_OP_CHECK_NO_PKT() /* not got secret for next packet yet */
     RX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT,
                       QRL_SUITE_AES128GCM, rx_script_5_1rtt_secret)
-    RX_OP_CHECK_PKT_N(5c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(5c)
     RX_OP_CHECK_NO_PKT()
 
     /* Try injecting the packet again */
@@ -579,17 +619,17 @@ static const struct rx_test_op rx_script_5[] = {
      * Initial packet is not output due to receiving a Handshake packet causing
      * auto-discard of Initial keys
      */
-    RX_OP_CHECK_PKT_N(5b)
-    RX_OP_CHECK_PKT_N(5c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(5b)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(5c)
     RX_OP_CHECK_NO_PKT()
     /* Try again with discarded keys */
     RX_OP_DISCARD_EL(QUIC_ENC_LEVEL_HANDSHAKE)
     RX_OP_INJECT_N(5)
-    RX_OP_CHECK_PKT_N(5c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(5c)
     RX_OP_CHECK_NO_PKT()
     /* Try again */
     RX_OP_INJECT_N(5)
-    RX_OP_CHECK_PKT_N(5c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(5c)
     RX_OP_CHECK_NO_PKT()
     /* Try again with discarded 1-RTT keys */
     RX_OP_DISCARD_EL(QUIC_ENC_LEVEL_1RTT)
@@ -602,15 +642,15 @@ static const struct rx_test_op rx_script_5[] = {
     RX_OP_INJECT_N(5)
     RX_OP_CHECK_NO_PKT()
     RX_OP_PROVIDE_SECRET_INITIAL(rx_script_5_c2s_init_dcid)
-    RX_OP_CHECK_PKT_N(5a)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(5a)
     RX_OP_CHECK_NO_PKT()
     RX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_HANDSHAKE,
                       QRL_SUITE_AES128GCM, rx_script_5_handshake_secret)
-    RX_OP_CHECK_PKT_N(5b)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(5b)
     RX_OP_CHECK_NO_PKT()
     RX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT,
                       QRL_SUITE_AES128GCM, rx_script_5_1rtt_secret)
-    RX_OP_CHECK_PKT_N(5c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(5c)
     RX_OP_CHECK_NO_PKT()
 
     RX_OP_DISCARD_EL(QUIC_ENC_LEVEL_HANDSHAKE)
@@ -925,15 +965,15 @@ static const struct rx_test_op rx_script_6[] = {
     RX_OP_ADD_RX_DCID(empty_conn_id)
     RX_OP_PROVIDE_SECRET_INITIAL(rx_script_6_c2s_init_dcid)
     RX_OP_INJECT_N(6)
-    RX_OP_CHECK_PKT_N(6a)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(6a)
     RX_OP_CHECK_NO_PKT() /* not got secret for next packet yet */
     RX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_HANDSHAKE,
                       QRL_SUITE_AES256GCM, rx_script_6_handshake_secret)
-    RX_OP_CHECK_PKT_N(6b)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(6b)
     RX_OP_CHECK_NO_PKT() /* not got secret for next packet yet */
     RX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT,
                       QRL_SUITE_AES256GCM, rx_script_6_1rtt_secret)
-    RX_OP_CHECK_PKT_N(6c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(6c)
     RX_OP_CHECK_NO_PKT()
 
     /* Try injecting the packet again */
@@ -942,17 +982,17 @@ static const struct rx_test_op rx_script_6[] = {
      * Initial packet is not output due to receiving a Handshake packet causing
      * auto-discard of Initial keys
      */
-    RX_OP_CHECK_PKT_N(6b)
-    RX_OP_CHECK_PKT_N(6c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(6b)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(6c)
     RX_OP_CHECK_NO_PKT()
     /* Try again with discarded keys */
     RX_OP_DISCARD_EL(QUIC_ENC_LEVEL_HANDSHAKE)
     RX_OP_INJECT_N(6)
-    RX_OP_CHECK_PKT_N(6c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(6c)
     RX_OP_CHECK_NO_PKT()
     /* Try again */
     RX_OP_INJECT_N(6)
-    RX_OP_CHECK_PKT_N(6c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(6c)
     RX_OP_CHECK_NO_PKT()
     /* Try again with discarded 1-RTT keys */
     RX_OP_DISCARD_EL(QUIC_ENC_LEVEL_1RTT)
@@ -965,15 +1005,15 @@ static const struct rx_test_op rx_script_6[] = {
     RX_OP_INJECT_N(6)
     RX_OP_CHECK_NO_PKT()
     RX_OP_PROVIDE_SECRET_INITIAL(rx_script_6_c2s_init_dcid)
-    RX_OP_CHECK_PKT_N(6a)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(6a)
     RX_OP_CHECK_NO_PKT()
     RX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_HANDSHAKE,
                       QRL_SUITE_AES256GCM, rx_script_6_handshake_secret)
-    RX_OP_CHECK_PKT_N(6b)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(6b)
     RX_OP_CHECK_NO_PKT()
     RX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT,
                       QRL_SUITE_AES256GCM, rx_script_6_1rtt_secret)
-    RX_OP_CHECK_PKT_N(6c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(6c)
     RX_OP_CHECK_NO_PKT()
 
     RX_OP_END
@@ -1282,15 +1322,15 @@ static const struct rx_test_op rx_script_7[] = {
     RX_OP_ADD_RX_DCID(empty_conn_id)
     RX_OP_PROVIDE_SECRET_INITIAL(rx_script_7_c2s_init_dcid)
     RX_OP_INJECT_N(7)
-    RX_OP_CHECK_PKT_N(7a)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(7a)
     RX_OP_CHECK_NO_PKT() /* not got secret for next packet yet */
     RX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_HANDSHAKE,
                       QRL_SUITE_CHACHA20POLY1305, rx_script_7_handshake_secret)
-    RX_OP_CHECK_PKT_N(7b)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(7b)
     RX_OP_CHECK_NO_PKT() /* not got secret for next packet yet */
     RX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT,
                       QRL_SUITE_CHACHA20POLY1305, rx_script_7_1rtt_secret)
-    RX_OP_CHECK_PKT_N(7c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(7c)
     RX_OP_CHECK_NO_PKT()
 
     /* Try injecting the packet again */
@@ -1299,17 +1339,17 @@ static const struct rx_test_op rx_script_7[] = {
      * Initial packet is not output due to receiving a Handshake packet causing
      * auto-discard of Initial keys
      */
-    RX_OP_CHECK_PKT_N(7b)
-    RX_OP_CHECK_PKT_N(7c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(7b)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(7c)
     RX_OP_CHECK_NO_PKT()
     /* Try again with discarded keys */
     RX_OP_DISCARD_EL(QUIC_ENC_LEVEL_HANDSHAKE)
     RX_OP_INJECT_N(7)
-    RX_OP_CHECK_PKT_N(7c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(7c)
     RX_OP_CHECK_NO_PKT()
     /* Try again */
     RX_OP_INJECT_N(7)
-    RX_OP_CHECK_PKT_N(7c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(7c)
     RX_OP_CHECK_NO_PKT()
     /* Try again with discarded 1-RTT keys */
     RX_OP_DISCARD_EL(QUIC_ENC_LEVEL_1RTT)
@@ -1322,15 +1362,15 @@ static const struct rx_test_op rx_script_7[] = {
     RX_OP_INJECT_N(7)
     RX_OP_CHECK_NO_PKT()
     RX_OP_PROVIDE_SECRET_INITIAL(rx_script_7_c2s_init_dcid)
-    RX_OP_CHECK_PKT_N(7a)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(7a)
     RX_OP_CHECK_NO_PKT()
     RX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_HANDSHAKE,
                       QRL_SUITE_CHACHA20POLY1305, rx_script_7_handshake_secret)
-    RX_OP_CHECK_PKT_N(7b)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(7b)
     RX_OP_CHECK_NO_PKT()
     RX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT,
                       QRL_SUITE_CHACHA20POLY1305, rx_script_7_1rtt_secret)
-    RX_OP_CHECK_PKT_N(7c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(7c)
     RX_OP_CHECK_NO_PKT()
 
     RX_OP_END
@@ -1535,14 +1575,14 @@ static const struct rx_test_op rx_script_8[] = {
     RX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT,
                          QRL_SUITE_AES128GCM, rx_script_8_1rtt_secret)
     /* Now the injected packet is successfully returned */
-    RX_OP_CHECK_PKT_N(8a)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(8a)
     RX_OP_CHECK_NO_PKT()
     RX_OP_CHECK_KEY_EPOCH(0)
 
     /* Packet with new key phase */
     RX_OP_INJECT_N(8b)
     /* Packet is successfully decrypted and returned */
-    RX_OP_CHECK_PKT_N(8b)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(8b)
     RX_OP_CHECK_NO_PKT()
     /* Key epoch has increased */
     RX_OP_CHECK_KEY_EPOCH(1)
@@ -1553,20 +1593,20 @@ static const struct rx_test_op rx_script_8[] = {
      */
     RX_OP_INJECT_N(8c)
     /* Should still be decrypted OK */
-    RX_OP_CHECK_PKT_N(8c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(8c)
     RX_OP_CHECK_NO_PKT()
     /* Epoch has not changed */
     RX_OP_CHECK_KEY_EPOCH(1)
 
     /* Another packet with the new keys. */
     RX_OP_INJECT_N(8d)
-    RX_OP_CHECK_PKT_N(8d)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(8d)
     RX_OP_CHECK_NO_PKT()
     RX_OP_CHECK_KEY_EPOCH(1)
 
     /* We can inject the old packet multiple times and it still works */
     RX_OP_INJECT_N(8c)
-    RX_OP_CHECK_PKT_N(8c)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(8c)
     RX_OP_CHECK_NO_PKT()
     RX_OP_CHECK_KEY_EPOCH(1)
 
@@ -1587,13 +1627,13 @@ static const struct rx_test_op rx_script_8[] = {
     /* Move from COOLDOWN to NORMAL and try again */
     RX_OP_KEY_UPDATE_TIMEOUT(1)
     RX_OP_INJECT_N(8e)
-    RX_OP_CHECK_PKT_N(8e)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(8e)
     RX_OP_CHECK_NO_PKT()
     RX_OP_CHECK_KEY_EPOCH(2)
 
     /* Can still receive old packet */
     RX_OP_INJECT_N(8d)
-    RX_OP_CHECK_PKT_N(8d)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(8d)
     RX_OP_CHECK_NO_PKT()
     RX_OP_CHECK_KEY_EPOCH(2)
 
@@ -1602,7 +1642,7 @@ static const struct rx_test_op rx_script_8[] = {
 
     /* Try a packet from epoch 3 */
     RX_OP_INJECT_N(8f)
-    RX_OP_CHECK_PKT_N(8f)
+    RX_OP_CHECK_PKT_FRAMES_OK_N(8f)
     RX_OP_CHECK_NO_PKT()
     RX_OP_CHECK_KEY_EPOCH(3)
 
@@ -1660,18 +1700,49 @@ static int cmp_pkt_hdr(const QUIC_PKT_HDR *a, const QUIC_PKT_HDR *b,
 }
 
 struct rx_state {
-    QUIC_DEMUX     *demux;
-    OSSL_QRX       *qrx;
-    OSSL_QRX_ARGS   args;
+    QUIC_DEMUX         *demux;
+
+    /* OSSL_QRX with necessary data */
+    OSSL_QRX           *qrx;
+    OSSL_QRX_ARGS       args;
+
+    /* OSSL_ACKM with necessary data */
+    OSSL_ACKM          *ackm;
+    OSSL_CC_DATA       *ccdata;
+    OSSL_STATM          statm;       /* NOT the state machine! */
+
+    /* Used for the RX depacketizer, and wraps the |qrx| and |ackm| */
+    SSL_CTX            *quic_ssl_ctx;
+    QUIC_CONNECTION    *quic_conn;
 };
 
 static void rx_state_teardown(struct rx_state *s)
 {
+    if (s->ackm != NULL) {
+        ossl_ackm_free(s->ackm);
+        ossl_quic_conn_set_ackm(s->quic_conn, NULL);
+        s->ackm = NULL;
+    }
+    if (s->ccdata != NULL) {
+        ossl_cc_dummy_method.free(s->ccdata);
+        s->ccdata = NULL;
+    }
+
     if (s->qrx != NULL) {
         ossl_qrx_free(s->qrx);
+        ossl_quic_conn_set_qrx(s->quic_conn, NULL);
         s->qrx = NULL;
     }
 
+    if (s->quic_conn != NULL) {
+        SSL_free((SSL *)s->quic_conn);
+        s->quic_conn = NULL;
+    }
+    if (s->quic_ssl_ctx != NULL) {
+        SSL_CTX_free(s->quic_ssl_ctx);
+        s->quic_ssl_ctx = NULL;
+    }
+
     if (s->demux != NULL) {
         ossl_quic_demux_free(s->demux);
         s->demux = NULL;
@@ -1680,6 +1751,13 @@ static void rx_state_teardown(struct rx_state *s)
 
 static uint64_t time_counter = 0;
 
+static OSSL_TIME fake_now(void *ignored)
+{
+    OSSL_TIME f = {0};
+
+    return f;
+}
+
 static OSSL_TIME expected_time(uint64_t counter)
 {
     return ossl_time_multiply(ossl_ticks2time(OSSL_TIME_MS), counter);
@@ -1703,6 +1781,7 @@ static int rx_state_ensure(struct rx_state *s)
     s->args.demux           = s->demux;
     s->args.max_deferred    = 32;
 
+    /* Initialise OSSL_QRX */
     if (s->qrx == NULL
         && !TEST_ptr(s->qrx = ossl_qrx_new(&s->args)))
         return 0;
@@ -1710,6 +1789,33 @@ static int rx_state_ensure(struct rx_state *s)
     return 1;
 }
 
+static int rx_state_ensure_for_frames(struct rx_state *s)
+{
+    SSL *qs;
+
+    if (!rx_state_ensure(s))
+        return 0;
+
+    /* Initialise ACK manager and congestion controller. */
+    if ((s->ccdata == NULL
+         && !TEST_ptr(s->ccdata = ossl_cc_dummy_method.new(NULL, NULL, NULL)))
+        || (s->ackm == NULL
+            && !TEST_ptr(s->ackm = ossl_ackm_new(fake_now, NULL, &s->statm,
+                                                 &ossl_cc_dummy_method,
+                                                 s->ccdata))))
+        return 0;
+
+    if (s->quic_conn == NULL
+        && (!TEST_ptr(s->quic_ssl_ctx
+                      = SSL_CTX_new_ex(NULL, NULL, OSSL_QUIC_client_method()))
+            || !TEST_ptr(qs = SSL_new(s->quic_ssl_ctx))
+            || !TEST_ptr(s->quic_conn = ossl_quic_conn_from_ssl(qs))
+            || !TEST_true(ossl_quic_conn_set_qrx(s->quic_conn, s->qrx))
+            || !TEST_true(ossl_quic_conn_set_ackm(s->quic_conn, s->ackm))))
+        return 0;
+    return 1;
+}
+
 static int rx_run_script(const struct rx_test_op *script)
 {
     int testresult = 0, pkt_outstanding = 0;
@@ -1785,8 +1891,26 @@ static int rx_run_script(const struct rx_test_op *script)
                                            op->buf, op->buf_len, 1)))
                     goto err;
 
-                ossl_qrx_release_pkt(s.qrx, pkt.handle);
-                pkt_outstanding = 0;
+                switch (op->subop) {
+                case RX_TEST_OP_CHECK_PKT_FRAMES_OK:
+                    if (!TEST_true(rx_state_ensure_for_frames(&s)))
+                        goto err;
+                    pkt_outstanding = 0;
+                    if (!TEST_true(ossl_quic_handle_frames(s.quic_conn, &pkt)))
+                        goto err;
+                    break;
+                case RX_TEST_OP_CHECK_PKT_FRAMES_INVALID:
+                    if (!TEST_true(rx_state_ensure_for_frames(&s)))
+                        goto err;
+                    pkt_outstanding = 0;
+                    if (!TEST_false(ossl_quic_handle_frames(s.quic_conn, &pkt)))
+                        goto err;
+                    break;
+                default:
+                    pkt_outstanding = 0;
+                    ossl_qrx_release_pkt(s.qrx, pkt.handle);
+                    break;
+                }
                 break;
             case RX_TEST_OP_CHECK_NO_PKT:
                 if (!TEST_true(rx_state_ensure(&s)))