From 4532c064e4987c566160a779bb59f7a0dc130782 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Tue, 12 Aug 2025 10:40:06 +0200 Subject: [PATCH] MINOR: quic: centralize padding for HP sampling on packet building The below patch has simplified INITIAL padding on emission. Now, qc_prep_pkts() is responsible to activate padding for this case, and there is no more special case in qc_do_build_pkt() needed. commit 8bc339a6ad4702f2c39b2a78aaaff665d85c762b BUG/MAJOR: quic: fix INITIAL padding with probing packet only However, qc_do_build_pkt() may still activate padding on its own, to ensure that a packet is big enough so that header protection decryption can be performed by the peer. HP decryption is performed by extracting a sample from the ciphered packet, starting 4 bytes after PN offset. Sample length is 16 bytes as defined by TLS algos used by QUIC. Thus, a QUIC sender must ensures that length of packet number plus payload fields to be at least 4 bytes long. This is enough given that each packet is completed by a 16 bytes AEAD tag which can be part of the HP sample. This patch simplifies qc_do_build_pkt() by centralizing padding for this case in a single location. This is performed at the end of the function after payload is completed. The code is thus simpler. This is not a bug. However, it may be interesting to backport this patch up to 2.6, as qc_do_build_pkt() is a tedious function, in particular when dealing with padding generation, thus it may benefit greatly from simplification. (cherry picked from commit 1529ec1a25cc216f7613517e5e8c936852d18007) Signed-off-by: Christopher Faulet (cherry picked from commit 6dfdf20e00b491f6fc4708917024a51620850a09) Signed-off-by: Christopher Faulet (cherry picked from commit 4a2a5ee5dde65c8f3cfb2ce6765e93b9ae728388) Signed-off-by: Christopher Faulet --- include/haproxy/quic_conn-t.h | 3 +++ src/quic_tx.c | 38 +++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h index 337deb4..19539e5 100644 --- a/include/haproxy/quic_conn-t.h +++ b/include/haproxy/quic_conn-t.h @@ -140,6 +140,9 @@ enum quic_pkt_type { #define QUIC_PACKET_PNL_BITMASK 0x03 #define QUIC_PACKET_PN_MAXLEN 4 +/* TLS algo supported by QUIC uses a 16-bytes sample for HP. */ +#define QUIC_HP_SAMPLE_LEN 16 + /* * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 diff --git a/src/quic_tx.c b/src/quic_tx.c index 0ac02a4..65aff5d 100644 --- a/src/quic_tx.c +++ b/src/quic_tx.c @@ -1902,13 +1902,6 @@ static int qc_do_build_pkt(unsigned char *pos, const unsigned char *end, add_ping_frm = 1; len += 1; dglen += 1; - - /* Ensure packet is big enough so that header protection sample - * decryption can be performed. Note that +1 is for the PING - * frame. - */ - if (!padding && *pn_len + 1 < QUIC_PACKET_PN_MAXLEN) - len += padding_len = QUIC_PACKET_PN_MAXLEN - *pn_len - 1; } /* Handle Initial packet padding if necessary. */ @@ -1926,14 +1919,29 @@ static int qc_do_build_pkt(unsigned char *pos, const unsigned char *end, } } } - else if (len_frms && len_frms < QUIC_PACKET_PN_MAXLEN) { - len += padding_len = QUIC_PACKET_PN_MAXLEN - len_frms; - } - /* TODO qc_do_build_pkt() must rely on its argument instead of using QEL field. */ - else if (LIST_ISEMPTY(&frm_list) && !cc && !qel->pktns->tx.pto_probe) { - /* If there is no frame at all to follow, add at least a PADDING frame. */ - if (!ack_frm_len) - len += padding_len = QUIC_PACKET_PN_MAXLEN - *pn_len; + + /* RFC 9001 5.4.2. Header Protection Sample + * + * To ensure that sufficient data is available for sampling, packets are + * padded so that the combined lengths of the encoded packet number and + * protected payload is at least 4 bytes longer than the sample required + * for header protection. The cipher suites defined in [TLS13] -- other + * than TLS_AES_128_CCM_8_SHA256, for which a header protection scheme + * is not defined in this document -- have 16-byte expansions and 16- + * byte header protection samples. This results in needing at least 3 + * bytes of frames in the unprotected payload if the packet number is + * encoded on a single byte, or 2 bytes of frames for a 2-byte packet + * number encoding. + */ + + /* Add padding if packet is too small for HP sampling as specified + * above. QUIC TLS algos relies on 16 bytes sample extracted 4 bytes + * after PN offset. Thus, pn and payload must be at least 4 bytes long, + * so that the sample will be extracted as the AEAD tag. + */ + if (*pn_len + len < QUIC_PACKET_PN_MAXLEN + QUIC_HP_SAMPLE_LEN) { + padding_len = QUIC_PACKET_PN_MAXLEN + QUIC_HP_SAMPLE_LEN - (*pn_len + len); + len += padding_len; } if (pkt->type != QUIC_PACKET_TYPE_SHORT && !quic_enc_int(&pos, end, len)) -- 1.7.10.4