Commit c69b5af10a for openssl.org
commit c69b5af10a754051274ef2e5ed17f47753bd6ea9
Author: Nikolas Gauder <nikolas.gauder@tum.de>
Date: Wed Oct 21 18:42:30 2026 +0200
QUIC: resend the whole ClientHello after a Retry
When the client received a Retry, ch_retry() marked only its first
Initial packet as pseudo-lost, assuming that the ClientHello fits in
it. Since 3.5 the default key shares (X25519MLKEM768 and X25519) put
the ClientHello into two Initial packets. The second one stayed in
flight, so the client resent only the first part of the ClientHello,
and the server could do no more than acknowledge it. The rest was sent
only once that acknowledgement had the second packet declared lost:
one more round trip for every Retry, and a congestion event that halved
the congestion window although nothing was lost.
RFC 9002 section 6.3 has a client that receives a Retry reset its loss
recovery state, as the server processed none of its Initial packets.
Mark every Initial packet still in flight as pseudo-lost instead.
ch_restart() marked only the first Initial packet after version
negotiation in the same way. It now shares the helper, so ch_retry()
no longer needs drop_later_pn to work out which packet number to mark.
A Version Negotiation packet and a Retry can be processed in the same
batch of datagrams. The first then leaves no Initial packet in flight
for the second, and their data is already queued, so that is not an
error. test_quic_retry_after_version_neg covers this order.
The SSL_trace reference recorded the extra round trip, and is updated.
The radix forbidden_frame_types script injects a frame into the
server's Initial packets. With the whole ClientHello sent at once, a
post-quantum ServerHello can fill the server's only Initial packet,
leaving no room, so the script's server now uses classical groups.
Fixes: f538b4215528 "QUIC_CHANNEL: Implementation"
Fixes #33077
Assisted-by: Claude:claude-opus-5-5
Reviewed-by: Mounir Idrassi <mounir.idrassi@idrix.fr>
Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
Merge-date: Fri Oct 23 12:53:54 2026
Merged-from: https://github.com/openssl/openssl/pull/33298
diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c
index 8f363c093a..c45c772818 100644
--- a/ssl/quic/quic_channel.c
+++ b/ssl/quic/quic_channel.c
@@ -46,8 +46,7 @@ static OSSL_TIME ch_determine_next_tick_deadline(QUIC_CHANNEL *ch);
static int ch_retry(QUIC_CHANNEL *ch,
const unsigned char *retry_token,
size_t retry_token_len,
- const QUIC_CONN_ID *retry_scid,
- int drop_later_pn);
+ const QUIC_CONN_ID *retry_scid);
static int ch_restart(QUIC_CHANNEL *ch);
static void ch_cleanup(QUIC_CHANNEL *ch);
@@ -75,8 +74,7 @@ static void rxku_detected(QUIC_PN pn, void *arg);
static int ch_retry(QUIC_CHANNEL *ch,
const unsigned char *retry_token,
size_t retry_token_len,
- const QUIC_CONN_ID *retry_scid,
- int drop_later_pn);
+ const QUIC_CONN_ID *retry_scid);
static void ch_update_idle(QUIC_CHANNEL *ch);
static int ch_discard_el(QUIC_CHANNEL *ch,
uint32_t enc_level);
@@ -2628,7 +2626,7 @@ static void ch_rx_handle_packet(QUIC_CHANNEL *ch, int channel_only)
if (!ch_retry(ch, ch->qrx_pkt->hdr->data,
ch->qrx_pkt->hdr->len - QUIC_RETRY_INTEGRITY_TAG_LEN,
- &ch->qrx_pkt->hdr->src_conn_id, old_have_processed_any_pkt))
+ &ch->qrx_pkt->hdr->src_conn_id))
ossl_quic_channel_raise_protocol_error(ch, OSSL_QUIC_ERR_INTERNAL_ERROR,
0, "handling retry packet");
break;
@@ -2996,9 +2994,29 @@ void ossl_quic_channel_local_close(QUIC_CHANNEL *ch, uint64_t app_error_code,
ch_start_terminating(ch, &tcause, 0);
}
+/*
+ * Force the ACKM to consider every Initial packet still in flight as lost, so
+ * that the CRYPTO data they carried is sent again. None may be in flight, e.g.
+ * when a Version Negotiation packet has just had us mark them all and a Retry
+ * follows before we send again; their data is then already queued.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+static int ch_mark_initial_pkts_pseudo_lost(QUIC_CHANNEL *ch)
+{
+ OSSL_ACKM *ackm = ch->ackm;
+ QUIC_PN pn;
+
+ while (ossl_ackm_get_largest_unacked(ackm, QUIC_PN_SPACE_INITIAL, &pn))
+ if (!ossl_ackm_mark_packet_pseudo_lost(ackm, QUIC_PN_SPACE_INITIAL, pn))
+ return 0;
+
+ return 1;
+}
+
/**
* ch_restart - Restarts the QUIC channel by simulating loss of the initial
- * packet. This forces the packet to be regenerated with the updated protocol
+ * packets. This forces them to be regenerated with the updated protocol
* version number.
*
* @ch: Pointer to the QUIC_CHANNEL structure.
@@ -3008,22 +3026,19 @@ void ossl_quic_channel_local_close(QUIC_CHANNEL *ch, uint64_t app_error_code,
static int ch_restart(QUIC_CHANNEL *ch)
{
/*
- * Just pretend we lost our initial packet, so it gets
+ * Just pretend we lost our initial packets, so they get
* regenerated, with our updated protocol version number
*/
- return ossl_ackm_mark_packet_pseudo_lost(ch->ackm, QUIC_PN_SPACE_INITIAL,
- /* PN= */ 0);
+ return ch_mark_initial_pkts_pseudo_lost(ch);
}
/* Called when a server asks us to do a retry. */
static int ch_retry(QUIC_CHANNEL *ch,
const unsigned char *retry_token,
size_t retry_token_len,
- const QUIC_CONN_ID *retry_scid,
- int drop_later_pn)
+ const QUIC_CONN_ID *retry_scid)
{
void *buf;
- QUIC_PN pn = 0;
/*
* RFC 9000 s. 17.2.5.1: "A client MUST discard a Retry packet that contains
@@ -3060,24 +3075,16 @@ static int ch_retry(QUIC_CHANNEL *ch,
ch->doing_retry = 1;
/*
- * If a retry isn't our first response, we need to drop packet number
- * one instead (i.e. the case where we did version negotiation first
- */
- if (drop_later_pn == 1)
- pn = 1;
-
- /*
- * We need to stimulate the Initial EL to generate the first CRYPTO frame
+ * We need to stimulate the Initial EL to generate the CRYPTO frames
* again. We can do this most cleanly by simply forcing the ACKM to consider
- * the first Initial packet as lost, which it effectively was as the server
- * hasn't processed it. This also maintains the desired behaviour with e.g.
- * PNs not resetting and so on.
+ * every Initial packet we have sent as lost, which they effectively were as
+ * the server hasn't processed them (RFC 9002 s. 6.3). This also maintains
+ * the desired behaviour with e.g. PNs not resetting and so on.
*
- * The PN we used initially is always zero, because QUIC does not allow
- * repeated retries.
+ * The ClientHello may span more than one Initial packet, so all of them
+ * must be marked, not only the first.
*/
- if (!ossl_ackm_mark_packet_pseudo_lost(ch->ackm, QUIC_PN_SPACE_INITIAL,
- pn))
+ if (!ch_mark_initial_pkts_pseudo_lost(ch))
return 0;
/*
diff --git a/test/quicapitest.c b/test/quicapitest.c
index 9a2e84646a..2f593fe010 100644
--- a/test/quicapitest.c
+++ b/test/quicapitest.c
@@ -26,6 +26,7 @@
#include "internal/quic_ssl.h"
#include "internal/quic_port.h"
#include "internal/quic_txp.h"
+#include "internal/quic_cc.h"
static OSSL_LIB_CTX *libctx = NULL;
static char *propq = NULL;
@@ -3999,6 +4000,342 @@ err:
return ret;
}
+/*
+ * Test that after a Retry the client sends its whole ClientHello again at
+ * once, also when it spans more than one Initial packet. The server should
+ * then be able to answer the client's first flight after the Retry with its
+ * Handshake packets, without hearing from the client again. Nothing is lost,
+ * so the client's congestion window must not shrink either.
+ */
+static int test_quic_retry_split_client_hello(void)
+{
+#if !defined(OPENSSL_NO_ML_KEM) && !defined(OPENSSL_NO_ECX)
+ SSL_CTX *cctx = NULL, *sctx = NULL;
+ SSL *clientssl = NULL, *serverssl = NULL, *qlistener = NULL;
+ QUIC_CHANNEL *cch, *sch;
+ QUIC_PN pn;
+ OSSL_PARAM params[2];
+ uint64_t cwnd = 0, init_cwnd;
+ int testresult = 0, i, ret;
+
+ if (!TEST_ptr(sctx = create_server_ctx())
+ || !TEST_ptr(cctx = create_client_ctx()))
+ goto err;
+
+ /* This key share puts the ClientHello into two Initial packets. */
+ if (!TEST_true(SSL_CTX_set1_groups_list(cctx, "X25519MLKEM768")))
+ goto err;
+
+ if (!create_quic_ssl_objects(sctx, cctx, &qlistener, &clientssl)
+ || !TEST_ptr(cch = ossl_quic_conn_get_channel(clientssl)))
+ goto err;
+
+ params[0] = OSSL_PARAM_construct_uint64(OSSL_CC_OPTION_CUR_CWND_SIZE,
+ &cwnd);
+ params[1] = OSSL_PARAM_construct_end();
+ if (!TEST_true(cch->cc_method->bind_diagnostics(cch->cc_data, params)))
+ goto err;
+
+ init_cwnd = cwnd;
+
+ /* Send ClientHello and server retry */
+ ret = SSL_connect(clientssl);
+ if (!TEST_int_le(ret, 0)
+ || !TEST_int_eq(SSL_get_error(clientssl, ret), SSL_ERROR_WANT_READ))
+ goto err;
+
+ /* The ClientHello must have needed more than one Initial packet. */
+ pn = ossl_quic_tx_packetiser_get_next_pn(cch->txp, QUIC_PN_SPACE_INITIAL);
+ if (!TEST_uint64_t_ge(pn, 2))
+ goto err;
+
+ SSL_handle_events(qlistener);
+
+ /* The client processes the Retry and sends its ClientHello again. */
+ ret = SSL_connect(clientssl);
+ if (!TEST_int_le(ret, 0)
+ || !TEST_int_eq(SSL_get_error(clientssl, ret), SSL_ERROR_WANT_READ))
+ goto err;
+
+ /*
+ * Only the server runs now, so it must make do with what the client sent
+ * right after the Retry. If that was only the first part of the
+ * ClientHello, the server can do no more than ACK it, and sends no
+ * Handshake packets.
+ */
+ for (i = 0; i < 8; i++)
+ SSL_handle_events(qlistener);
+
+ if (!TEST_ptr(serverssl = SSL_accept_connection(qlistener, 0))
+ || !TEST_ptr(sch = ossl_quic_conn_get_channel(serverssl)))
+ goto err;
+
+ pn = ossl_quic_tx_packetiser_get_next_pn(sch->txp, QUIC_PN_SPACE_HANDSHAKE);
+ if (!TEST_uint64_t_gt(pn, 0))
+ goto err;
+
+ if (!TEST_true(create_bare_ssl_connection(serverssl, clientssl,
+ SSL_ERROR_NONE, 0, 0))
+ || !TEST_uint64_t_ge(cwnd, init_cwnd))
+ goto err;
+
+ testresult = 1;
+
+err:
+ SSL_CTX_free(cctx);
+ SSL_CTX_free(sctx);
+ SSL_free(clientssl);
+ SSL_free(serverssl);
+ SSL_free(qlistener);
+
+ return testresult;
+#else
+ return TEST_skip("ML-KEM or X25519 is not supported in this build");
+#endif
+}
+
+#if !defined(OPENSSL_NO_ML_KEM) && !defined(OPENSSL_NO_ECX)
+struct initial_version {
+ size_t to_alter;
+ QUIC_PKT_HDR hdr;
+};
+
+/*
+ * Packet mutator giving the next to_alter Initial packets the client sends a
+ * version the server does not support, so that the server answers each of
+ * them with a Version Negotiation packet.
+ */
+static int alter_initial_version(const QUIC_PKT_HDR *hdrin,
+ const OSSL_QTX_IOVEC *iovecin, size_t numin,
+ QUIC_PKT_HDR **hdrout,
+ const OSSL_QTX_IOVEC **iovecout,
+ size_t *numout,
+ void *arg)
+{
+ struct initial_version *iv = arg;
+
+ iv->hdr = *hdrin;
+ if (iv->to_alter > 0 && hdrin->type == QUIC_PKT_TYPE_INITIAL) {
+ iv->hdr.version = 0xdeadbeef;
+ --iv->to_alter;
+ }
+
+ *hdrout = &iv->hdr;
+ *iovecout = iovecin;
+ *numout = numin;
+ return 1;
+}
+#endif
+
+/*
+ * Test that the client copes with a Version Negotiation packet and a Retry
+ * that it processes together, before it sends again. The ClientHello spans
+ * two Initial packets, and only the first has an unsupported version, so the
+ * server answers that one with a Version Negotiation packet and the second
+ * with a Retry. The Version Negotiation packet leaves no Initial packet in
+ * flight when the Retry follows, but the ClientHello is already queued to be
+ * sent again, so the handshake must still complete.
+ */
+static int test_quic_retry_after_version_neg(void)
+{
+#if !defined(OPENSSL_NO_ML_KEM) && !defined(OPENSSL_NO_ECX)
+ SSL_CTX *cctx = NULL, *sctx = NULL;
+ SSL *clientssl = NULL, *serverssl = NULL, *qlistener = NULL;
+ QUIC_CHANNEL *cch;
+ QUIC_PN pn;
+ struct initial_version iv = { 0 };
+ int testresult = 0, i, ret;
+
+ if (!TEST_ptr(sctx = create_server_ctx())
+ || !TEST_ptr(cctx = create_client_ctx()))
+ goto err;
+
+ /* This key share puts the ClientHello into two Initial packets. */
+ if (!TEST_true(SSL_CTX_set1_groups_list(cctx, "X25519MLKEM768")))
+ goto err;
+
+ if (!create_quic_ssl_objects(sctx, cctx, &qlistener, &clientssl)
+ || !TEST_ptr(cch = ossl_quic_conn_get_channel(clientssl))
+ || !TEST_true(ossl_quic_channel_set_mutator(cch,
+ alter_initial_version, NULL, &iv)))
+ goto err;
+
+ iv.to_alter = 1;
+ ret = SSL_connect(clientssl);
+ if (!TEST_int_le(ret, 0)
+ || !TEST_int_eq(SSL_get_error(clientssl, ret), SSL_ERROR_WANT_READ)
+ || !TEST_size_t_eq(iv.to_alter, 0))
+ goto err;
+
+ /* The ClientHello must have needed more than one Initial packet. */
+ pn = ossl_quic_tx_packetiser_get_next_pn(cch->txp, QUIC_PN_SPACE_INITIAL);
+ if (!TEST_uint64_t_ge(pn, 2))
+ goto err;
+
+ /* The server answers with a Version Negotiation packet and a Retry. */
+ SSL_handle_events(qlistener);
+
+ /* The client reads and processes both before it sends again. */
+ ret = SSL_connect(clientssl);
+ if (!TEST_int_le(ret, 0)
+ || !TEST_int_eq(SSL_get_error(clientssl, ret), SSL_ERROR_WANT_READ)
+ || !TEST_true(cch->doing_retry))
+ goto err;
+
+ for (i = 0; i < 8; i++)
+ SSL_handle_events(qlistener);
+
+ if (!TEST_ptr(serverssl = SSL_accept_connection(qlistener, 0))
+ || !TEST_true(create_bare_ssl_connection(serverssl, clientssl,
+ SSL_ERROR_NONE, 0, 0)))
+ goto err;
+
+ testresult = 1;
+
+err:
+ SSL_CTX_free(cctx);
+ SSL_CTX_free(sctx);
+ SSL_free(clientssl);
+ SSL_free(serverssl);
+ SSL_free(qlistener);
+
+ return testresult;
+#else
+ return TEST_skip("ML-KEM or X25519 is not supported in this build");
+#endif
+}
+
+/*
+ * Test that the client sends its whole ClientHello again after a Version
+ * Negotiation packet, also when it spans more than one Initial packet. Every
+ * Initial packet the client sends first has an unsupported version, so the
+ * server answers with Version Negotiation packets alone.
+ *
+ * Test 0: The server does not validate addresses, so it answers the
+ * ClientHello the client sends after Version Negotiation with its
+ * Handshake packets, without hearing from the client again.
+ * Test 1: The server answers that ClientHello with a Retry, which the client
+ * processes on a later tick. The Retry has the ClientHello sent again
+ * anyway, so the client must have sent it whole before.
+ */
+static int test_quic_version_neg_split_client_hello(int idx)
+{
+#if !defined(OPENSSL_NO_ML_KEM) && !defined(OPENSSL_NO_ECX)
+ SSL_CTX *cctx = NULL, *sctx = NULL;
+ SSL *clientssl = NULL, *serverssl = NULL, *qlistener = NULL;
+ BIO *sbio;
+ QUIC_CHANNEL *cch, *sch;
+ QUIC_PN first_pn, pn;
+ struct initial_version iv = { 0 };
+ int testresult = 0, i, ret;
+
+ if (!TEST_ptr(sctx = create_server_ctx())
+ || !TEST_ptr(cctx = create_client_ctx()))
+ goto err;
+
+ /* This key share puts the ClientHello into two Initial packets. */
+ if (!TEST_true(SSL_CTX_set1_groups_list(cctx, "X25519MLKEM768")))
+ goto err;
+
+ if (!create_quic_ssl_objects(sctx, cctx, &qlistener, &clientssl)
+ || !TEST_ptr(cch = ossl_quic_conn_get_channel(clientssl))
+ || !TEST_true(ossl_quic_channel_set_mutator(cch,
+ alter_initial_version, NULL, &iv)))
+ goto err;
+
+ if (idx == 0) {
+ /* Replace the listener with one that does not validate addresses. */
+ sbio = SSL_get_rbio(qlistener);
+ if (!TEST_true(BIO_up_ref(sbio)))
+ goto err;
+ SSL_free(qlistener);
+ qlistener = SSL_new_listener(sctx, SSL_LISTENER_FLAG_NO_VALIDATE);
+ if (!TEST_ptr(qlistener)) {
+ BIO_free(sbio);
+ goto err;
+ }
+ SSL_set_bio(qlistener, sbio, sbio);
+ if (!TEST_true(SSL_listen(qlistener)))
+ goto err;
+ }
+
+ iv.to_alter = SIZE_MAX;
+ ret = SSL_connect(clientssl);
+ if (!TEST_int_le(ret, 0)
+ || !TEST_int_eq(SSL_get_error(clientssl, ret), SSL_ERROR_WANT_READ))
+ goto err;
+ iv.to_alter = 0;
+
+ /* The ClientHello must have needed more than one Initial packet. */
+ first_pn = ossl_quic_tx_packetiser_get_next_pn(cch->txp,
+ QUIC_PN_SPACE_INITIAL);
+ if (!TEST_uint64_t_ge(first_pn, 2))
+ goto err;
+
+ /* The server answers with Version Negotiation packets. */
+ SSL_handle_events(qlistener);
+
+ /* The client processes them and sends its ClientHello again. */
+ ret = SSL_connect(clientssl);
+ if (!TEST_int_le(ret, 0)
+ || !TEST_int_eq(SSL_get_error(clientssl, ret), SSL_ERROR_WANT_READ)
+ || !TEST_false(cch->doing_retry))
+ goto err;
+
+ /*
+ * The whole ClientHello takes as many Initial packets as it did the first
+ * time. If only its first part was sent again, the rest waits for loss
+ * detection.
+ */
+ pn = ossl_quic_tx_packetiser_get_next_pn(cch->txp, QUIC_PN_SPACE_INITIAL);
+ if (!TEST_uint64_t_ge(pn - first_pn, first_pn))
+ goto err;
+
+ if (idx == 1) {
+ /* The server answers with a Retry, and the client processes it. */
+ SSL_handle_events(qlistener);
+
+ ret = SSL_connect(clientssl);
+ if (!TEST_int_le(ret, 0)
+ || !TEST_int_eq(SSL_get_error(clientssl, ret), SSL_ERROR_WANT_READ)
+ || !TEST_true(cch->doing_retry))
+ goto err;
+ }
+
+ /*
+ * Only the server runs now, so it must make do with what the client sent
+ * last.
+ */
+ for (i = 0; i < 8; i++)
+ SSL_handle_events(qlistener);
+
+ if (!TEST_ptr(serverssl = SSL_accept_connection(qlistener, 0))
+ || !TEST_ptr(sch = ossl_quic_conn_get_channel(serverssl)))
+ goto err;
+
+ pn = ossl_quic_tx_packetiser_get_next_pn(sch->txp, QUIC_PN_SPACE_HANDSHAKE);
+ if (!TEST_uint64_t_gt(pn, 0))
+ goto err;
+
+ if (!TEST_true(create_bare_ssl_connection(serverssl, clientssl,
+ SSL_ERROR_NONE, 0, 0)))
+ goto err;
+
+ testresult = 1;
+
+err:
+ SSL_CTX_free(cctx);
+ SSL_CTX_free(sctx);
+ SSL_free(clientssl);
+ SSL_free(serverssl);
+ SSL_free(qlistener);
+
+ return testresult;
+#else
+ return TEST_skip("ML-KEM or X25519 is not supported in this build");
+#endif
+}
+
static SSL *quic_verify_ssl = NULL;
static int quic_verify_cb(int ok, X509_STORE_CTX *ctx)
@@ -5088,6 +5425,9 @@ int setup_tests(void)
ADD_TEST(test_ssl_accept_connection);
ADD_TEST(test_quic_amplification_limit);
ADD_TEST(test_quic_retry_token_refused);
+ ADD_TEST(test_quic_retry_split_client_hello);
+ ADD_TEST(test_quic_retry_after_version_neg);
+ ADD_ALL_TESTS(test_quic_version_neg_split_client_hello, 2);
ADD_TEST(test_ssl_set_verify);
ADD_TEST(test_accept_stream);
ADD_TEST(test_reject_stream_gc);
diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c
index e2ce340776..ae6e92033d 100644
--- a/test/radix/quic_tests.c
+++ b/test/radix/quic_tests.c
@@ -2465,6 +2465,14 @@ DEF_FUNC(arm_forbidden_frame)
SSL_CTX_set_client_hello_cb(SSL_get_SSL_CTX(listener),
forbidden_frame_client_hello_cb, NULL);
+ /*
+ * A post-quantum key share makes the ServerHello large enough to fill the
+ * server's only Initial packet, leaving no room for the injected frame.
+ */
+ if (!TEST_true(SSL_CTX_set1_groups_list(SSL_get_SSL_CTX(listener),
+ "?X25519:?P-256:?ffdhe2048")))
+ goto err;
+
TEST_info("Packet type %llu, frame type %llu",
(unsigned long long)radix_fault.word0,
(unsigned long long)radix_fault.word1);
diff --git a/test/recipes/75-test_quicapi_data/ssltraceref-zlib.txt b/test/recipes/75-test_quicapi_data/ssltraceref-zlib.txt
index 1bce022109..7a6c75f853 100644
--- a/test/recipes/75-test_quicapi_data/ssltraceref-zlib.txt
+++ b/test/recipes/75-test_quicapi_data/ssltraceref-zlib.txt
@@ -126,7 +126,7 @@ Sent Packet
Packet Number: 0x00000002
Sent Frame: Crypto
Offset: 1098
- Len: 60
+ Len: ?
Sent Frame: Padding
Sent Packet
Packet Type: Initial
@@ -142,36 +142,6 @@ Sent Datagram
Length: 1200
Received Datagram
Length: 1200
-Received Packet
- Packet Type: Initial
- Version: 0x00000001
- Destination Conn Id: <zero length id>
- Source Conn Id: 0x?
- Payload length: 1178
- Token: <zero length token>
- Packet Number: 0x00000000
-Received Frame: Ack (without ECN)
- Largest acked: 3
- Ack delay (raw) 0
- Ack range count: 0
- First ack range: 1
-Received Frame: Padding
-Sent Frame: Crypto
- Offset: 1158
- Len: ?
-Sent Frame: Padding
-Sent Packet
- Packet Type: Initial
- Version: 0x00000001
- Destination Conn Id: 0x?
- Source Conn Id: <zero length id>
- Payload length: 1118
- Token: ?
- Packet Number: 0x00000004
-Sent Datagram
- Length: 1200
-Received Datagram
- Length: 1200
Received Datagram
Length: 1199
Received Datagram
@@ -183,7 +153,7 @@ Received Packet
Source Conn Id: 0x?
Payload length: 1178
Token: <zero length token>
- Packet Number: 0x00000001
+ Packet Number: 0x00000000
Received Packet
Packet Type: Initial
Version: 0x00000001
@@ -191,12 +161,12 @@ Received Packet
Source Conn Id: 0x?
Payload length: 45
Token: <zero length token>
- Packet Number: 0x00000002
+ Packet Number: 0x00000001
Received Frame: Ack (without ECN)
- Largest acked: 4
+ Largest acked: 3
Ack delay (raw) 0
Ack range count: 0
- First ack range: 2
+ First ack range: 1
Received Frame: Crypto
Offset: 0
Len: 1153
@@ -373,10 +343,10 @@ Header:
verify_data (len=32): ?
Sent Frame: Ack (without ECN)
- Largest acked: 2
+ Largest acked: 1
Ack delay (raw) 0
Ack range count: 0
- First ack range: 2
+ First ack range: 1
Sent Frame: Ack (without ECN)
Largest acked: 1
Ack delay (raw) 0
@@ -393,7 +363,7 @@ Sent Packet
Source Conn Id: <zero length id>
Payload length: 1037
Token: ?
- Packet Number: 0x00000005
+ Packet Number: 0x00000004
Sent Packet
Packet Type: Handshake
Version: 0x00000001
diff --git a/test/recipes/75-test_quicapi_data/ssltraceref.txt b/test/recipes/75-test_quicapi_data/ssltraceref.txt
index 300b4ca456..c3d56b3fa7 100644
--- a/test/recipes/75-test_quicapi_data/ssltraceref.txt
+++ b/test/recipes/75-test_quicapi_data/ssltraceref.txt
@@ -124,7 +124,7 @@ Sent Packet
Packet Number: 0x00000002
Sent Frame: Crypto
Offset: 1098
- Len: 60
+ Len: ?
Sent Frame: Padding
Sent Packet
Packet Type: Initial
@@ -140,36 +140,6 @@ Sent Datagram
Length: 1200
Received Datagram
Length: 1200
-Received Packet
- Packet Type: Initial
- Version: 0x00000001
- Destination Conn Id: <zero length id>
- Source Conn Id: 0x?
- Payload length: 1178
- Token: <zero length token>
- Packet Number: 0x00000000
-Received Frame: Ack (without ECN)
- Largest acked: 3
- Ack delay (raw) 0
- Ack range count: 0
- First ack range: 1
-Received Frame: Padding
-Sent Frame: Crypto
- Offset: 1158
- Len: ?
-Sent Frame: Padding
-Sent Packet
- Packet Type: Initial
- Version: 0x00000001
- Destination Conn Id: 0x?
- Source Conn Id: <zero length id>
- Payload length: 1118
- Token: ?
- Packet Number: 0x00000004
-Sent Datagram
- Length: 1200
-Received Datagram
- Length: 1200
Received Datagram
Length: 1199
Received Datagram
@@ -181,7 +151,7 @@ Received Packet
Source Conn Id: 0x?
Payload length: 1178
Token: <zero length token>
- Packet Number: 0x00000001
+ Packet Number: 0x00000000
Received Packet
Packet Type: Initial
Version: 0x00000001
@@ -189,12 +159,12 @@ Received Packet
Source Conn Id: 0x?
Payload length: 45
Token: <zero length token>
- Packet Number: 0x00000002
+ Packet Number: 0x00000001
Received Frame: Ack (without ECN)
- Largest acked: 4
+ Largest acked: 3
Ack delay (raw) 0
Ack range count: 0
- First ack range: 2
+ First ack range: 1
Received Frame: Crypto
Offset: 0
Len: 1153
@@ -371,10 +341,10 @@ Header:
verify_data (len=32): ?
Sent Frame: Ack (without ECN)
- Largest acked: 2
+ Largest acked: 1
Ack delay (raw) 0
Ack range count: 0
- First ack range: 2
+ First ack range: 1
Sent Frame: Ack (without ECN)
Largest acked: 1
Ack delay (raw) 0
@@ -391,7 +361,7 @@ Sent Packet
Source Conn Id: <zero length id>
Payload length: 1037
Token: ?
- Packet Number: 0x00000005
+ Packet Number: 0x00000004
Sent Packet
Packet Type: Handshake
Version: 0x00000001