MINOR: quic: centralize padding for HP sampling on packet building
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 12 Aug 2025 08:40:06 +0000 (10:40 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Wed, 1 Oct 2025 14:48:33 +0000 (16:48 +0200)
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 <cfaulet@haproxy.com>
(cherry picked from commit 6dfdf20e00b491f6fc4708917024a51620850a09)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 4a2a5ee5dde65c8f3cfb2ce6765e93b9ae728388)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>

include/haproxy/quic_conn-t.h
src/quic_tx.c

index 337deb4..19539e5 100644 (file)
@@ -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
index 0ac02a4..65aff5d 100644 (file)
@@ -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 <probe> argument instead of using QEL <pto_probe> 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))