]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: quic: use the same HP encryption helpers for RX and TX
authorFrederic Lecaille <flecaille@haproxy.com>
Fri, 16 Oct 2026 09:41:41 +0000 (11:41 +0200)
committerFrederic Lecaille <flecaille@haproxy.com>
Fri, 16 Oct 2026 14:42:55 +0000 (16:42 +0200)
Header protection is applied by generating a mask and XORing it with
the header. Removing it requires the same mask and the same XOR, which
restores the original header.

With AES-128-CTR, AES-256-CTR and ChaCha20, encryption and decryption
produce the same mask from the same key and packet sample. This is a
property of these algorithms, not specific to OpenSSL.

Use quic_tls_enc_hp_ctx_init() and quic_tls_hp_encrypt() for RX too,
with the RX key, and remove the redundant decryption helpers.

include/haproxy/quic_tls.h
src/quic_rx.c
src/quic_ssl.c
src/quic_tls.c

index e0abc2355d56f2f3c655b7f9ec2817c7917588b7..1332f85093f557876557385c69b8fa5893122a66 100644 (file)
@@ -120,14 +120,9 @@ int quic_tls_sec_update(const EVP_MD *md, const struct quic_version *qv,
 void quic_aead_iv_build(unsigned char *iv, size_t ivlen,
                         unsigned char *aead_iv, size_t aead_ivlen, uint64_t pn);
 
-/* HP protection (AES) */
-int quic_tls_dec_hp_ctx_init(EVP_CIPHER_CTX **aes_ctx,
-                              const EVP_CIPHER *aes, unsigned char *key);
+/* Header protection: encryption generates the mask for both TX and RX. */
 int quic_tls_enc_hp_ctx_init(EVP_CIPHER_CTX **aes_ctx,
                               const EVP_CIPHER *aes, unsigned char *key);
-int quic_tls_hp_decrypt(unsigned char *out,
-                         const unsigned char *in, size_t inlen,
-                         EVP_CIPHER_CTX *ctx, unsigned char *key);
 int quic_tls_hp_encrypt(unsigned char *out,
                          const unsigned char *in, size_t inlen,
                          EVP_CIPHER_CTX *ctx, unsigned char *key);
index 5fcb213e3ecc89376d65be14bf7e21e4f4111ab0..e90c76e094a113e1c98ba7912664bbadab2d0e7d 100644 (file)
@@ -95,7 +95,7 @@ static int qc_do_rm_hp(struct quic_conn *qc,
 
        sample = pn + QUIC_PACKET_PN_MAXLEN;
 
-       if (!quic_tls_hp_decrypt(mask, sample, sizeof mask, tls_ctx->rx.hp_ctx, tls_ctx->rx.hp_key)) {
+       if (!quic_tls_hp_encrypt(mask, sample, sizeof mask, tls_ctx->rx.hp_ctx, tls_ctx->rx.hp_key)) {
                TRACE_ERROR("HP removing failed", QUIC_EV_CONN_RMHP, qc, pkt);
                goto leave;
        }
index c329bffe91e12f823aa322313ea1cd5ee2eb7a4f..09fff51c84be02a4457c98e79e9a427e2d58e8d0 100644 (file)
@@ -250,7 +250,7 @@ static int ha_quic_set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t
                goto leave;
        }
 
-       if (!quic_tls_dec_hp_ctx_init(&rx->hp_ctx, rx->hp, rx->hp_key)) {
+       if (!quic_tls_enc_hp_ctx_init(&rx->hp_ctx, rx->hp, rx->hp_key)) {
                TRACE_ERROR("could not initial RX TLS cipher context for HP", QUIC_EV_CONN_RWSEC, qc);
                goto leave;
        }
index 0838ffa6bbd9f03f7aaf68c22a2d9935b421bf0b..9f1726c25a6715c210a6b44ec30ede220717e453 100644 (file)
@@ -696,7 +696,9 @@ int quic_tls_rx_ctx_init(QUIC_AEAD_CTX **rx_ctx,
        return 0;
 }
 
-/* Initialize <*hp_ctx> cipher context with <key> as key for header protection encryption */
+/* Initialize <*hp_ctx> with <key> for header protection mask generation,
+ * both when applying (TX) and removing (RX) header protection.
+ */
 int quic_tls_enc_hp_ctx_init(EVP_CIPHER_CTX **hp_ctx,
                               const EVP_CIPHER *hp, unsigned char *key)
 {
@@ -725,9 +727,9 @@ int quic_tls_enc_hp_ctx_init(EVP_CIPHER_CTX **hp_ctx,
        return 0;
 }
 
-/* Encrypt <inlen> bytes from <in> buffer into <out> with <ctx> as
- * cipher context. This is the responsibility of the caller to check there
- * is at least <inlen> bytes of available space in <out> buffer.
+/* Generate the header protection mask in <out> using <in> as the sample.
+ * The caller must provide <inlen> zero bytes in <out>.
+ * The same operation is used for TX and RX, with their respective keys.
  * Return 1 if succeeded, 0 if not.
  */
 int quic_tls_hp_encrypt(unsigned char *out,
@@ -763,73 +765,6 @@ int quic_tls_hp_encrypt(unsigned char *out,
        return 1;
 }
 
-/* Initialize <*hp_ctx> cipher context with <key> as key for header protection decryption */
-int quic_tls_dec_hp_ctx_init(EVP_CIPHER_CTX **hp_ctx,
-                              const EVP_CIPHER *hp, unsigned char *key)
-{
-       EVP_CIPHER_CTX *ctx;
-
-#ifdef QUIC_AEAD_API
-
-       if (hp == EVP_CIPHER_CHACHA20) {
-               *hp_ctx = EVP_CIPHER_CTX_CHACHA20;
-               return 1;
-       }
-#endif
-
-       ctx = EVP_CIPHER_CTX_new();
-       if (!ctx)
-               return 0;
-
-       if (!EVP_DecryptInit_ex(ctx, hp, NULL, key, NULL))
-               goto err;
-
-       *hp_ctx = ctx;
-       return 1;
-
- err:
-       EVP_CIPHER_CTX_free(ctx);
-       return 0;
-}
-
-/* Decrypt <in> data into <out> with <ctx> as cipher context.
- * This is the responsibility of the caller to check there is at least
- * <outlen> bytes into <in> buffer.
- * Return 1 if succeeded, 0 if not.
- */
-int quic_tls_hp_decrypt(unsigned char *out,
-                         const unsigned char *in, size_t inlen,
-                         EVP_CIPHER_CTX *ctx, unsigned char *key)
-{
-       int ret = 0;
-
-#ifdef QUIC_AEAD_API
-       if (ctx == EVP_CIPHER_CTX_CHACHA20) {
-               uint32_t counter;
-
-               /* According to RFC 9001, 5.4.4. ChaCha20-Based Header Protection:
-                * The first 4 bytes of the sampled ciphertext are the block counter.
-                * The remaining 12 bytes are used as the nonce.
-                */
-#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-               counter = (uint32_t)in[0] + (uint32_t)(in[1] << 8) + (uint32_t)(in[2] << 16) + (uint32_t)(in[3] << 24);
-#else
-               memcpy(&counter, in, sizeof(counter));
-#endif
-               CRYPTO_chacha_20(out, out, inlen, key, in + sizeof(counter), counter);
-               return 1;
-       }
-
-#endif
-
-       if (!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, in) ||
-           !EVP_DecryptUpdate(ctx, out, &ret, out, inlen) ||
-           !EVP_DecryptFinal_ex(ctx, out, &ret))
-               return 0;
-
-       return 1;
-}
-
 /* Initialize the cipher context for TX part of <tls_ctx> QUIC TLS context.
  * Return 1 if succeeded, 0 if not.
  */