MINOR: quic_tls: Add reusable cipher contexts to QUIC TLS contexts

Add ->ctx new member field to quic_tls_secrets struct to store the cipher context
for each QUIC TLS context TX/RX parts.
Add quic_tls_rx_ctx_init() and quic_tls_tx_ctx_init() functions to initialize
these cipher context for RX and TX parts respectively.
Make qc_new_isecs() call these two functions to initialize the cipher contexts
of the Initial secrets. Same thing for ha_quic_set_encryption_secrets() to
initialize the cipher contexts of the subsequent derived secrets (ORTT, Handshake,
1RTT).
Modify quic_tls_decrypt() and quic_tls_encrypt() to always use the same cipher
context without allocating it each time they are called.
diff --git a/include/haproxy/quic_tls-t.h b/include/haproxy/quic_tls-t.h
index 7dab78d..59a8186 100644
--- a/include/haproxy/quic_tls-t.h
+++ b/include/haproxy/quic_tls-t.h
@@ -112,6 +112,7 @@
 #define QUIC_FL_TLS_SECRETS_DCD  (1 << 2)
 
 struct quic_tls_secrets {
+	EVP_CIPHER_CTX *ctx;
 	const EVP_CIPHER *aead;
 	const EVP_MD *md;
 	const EVP_CIPHER *hp;
diff --git a/include/haproxy/quic_tls.h b/include/haproxy/quic_tls.h
index b4a6675..d12d6e0 100644
--- a/include/haproxy/quic_tls.h
+++ b/include/haproxy/quic_tls.h
@@ -61,12 +61,12 @@
 
 int quic_tls_encrypt(unsigned char *buf, size_t len,
                      const unsigned char *aad, size_t aad_len,
-                     const EVP_CIPHER *aead,
+                     EVP_CIPHER_CTX *ctx, const EVP_CIPHER *aead,
                      const unsigned char *key, const unsigned char *iv);
 
 int quic_tls_decrypt(unsigned char *buf, size_t len,
                      unsigned char *aad, size_t aad_len,
-                     const EVP_CIPHER *aead,
+                     EVP_CIPHER_CTX *tls_ctx, const EVP_CIPHER *aead,
                      const unsigned char *key, const unsigned char *iv);
 
 int quic_tls_generate_retry_integrity_tag(unsigned char *odcid, size_t odcid_len,
@@ -79,6 +79,11 @@
                          unsigned char *hp_key, size_t hp_keylen,
                          const unsigned char *secret, size_t secretlen);
 
+int quic_tls_rx_ctx_init(EVP_CIPHER_CTX **rx_ctx,
+                         const EVP_CIPHER *aead, unsigned char *key);
+int quic_tls_tx_ctx_init(EVP_CIPHER_CTX **tx_ctx,
+                         const EVP_CIPHER *aead, unsigned char *key);
+
 int quic_tls_sec_update(const EVP_MD *md,
                         unsigned char *new_sec, size_t new_seclen,
                         const unsigned char *sec, size_t seclen);
@@ -370,10 +375,15 @@
 		memset(ctx->tx.key, 0, ctx->tx.keylen);
 		ctx->tx.keylen = 0;
 	}
+
+	EVP_CIPHER_CTX_free(ctx->rx.ctx);
 	pool_free(pool_head_quic_tls_iv,  ctx->rx.iv);
 	pool_free(pool_head_quic_tls_key, ctx->rx.key);
+
+	EVP_CIPHER_CTX_free(ctx->tx.ctx);
 	pool_free(pool_head_quic_tls_iv,  ctx->tx.iv);
 	pool_free(pool_head_quic_tls_key, ctx->tx.key);
+
 	ctx->rx.iv = ctx->tx.iv = NULL;
 	ctx->rx.key = ctx->tx.key = NULL;
 }
@@ -507,6 +517,9 @@
 	                          rx_init_sec, sizeof rx_init_sec))
 		goto err;
 
+	if (!quic_tls_rx_ctx_init(&rx_ctx->ctx, rx_ctx->aead, rx_ctx->key))
+		goto err;
+
 	if (!quic_tls_derive_keys(ctx->tx.aead, ctx->tx.hp, ctx->tx.md,
 	                          tx_ctx->key, tx_ctx->keylen,
 	                          tx_ctx->iv, tx_ctx->ivlen,
@@ -514,6 +527,9 @@
 	                          tx_init_sec, sizeof tx_init_sec))
 		goto err;
 
+	if (!quic_tls_tx_ctx_init(&tx_ctx->ctx, tx_ctx->aead, tx_ctx->key))
+		goto err;
+
 	ctx->flags |= QUIC_FL_TLS_SECRETS_SET;
 	TRACE_LEAVE(QUIC_EV_CONN_ISEC, NULL, rx_init_sec, tx_init_sec);
 
diff --git a/src/quic_tls.c b/src/quic_tls.c
index d4314d7..cff461c 100644
--- a/src/quic_tls.c
+++ b/src/quic_tls.c
@@ -303,6 +303,64 @@
 	return 1;
 }
 
+/* Initialize the cipher context for RX part of <tls_ctx> QUIC TLS context.
+ * Return 1 if succeeded, 0 if not.
+ */
+int quic_tls_rx_ctx_init(EVP_CIPHER_CTX **rx_ctx,
+                         const EVP_CIPHER *aead, unsigned char *key)
+{
+	EVP_CIPHER_CTX *ctx;
+	int aead_nid = EVP_CIPHER_nid(aead);
+
+	ctx = EVP_CIPHER_CTX_new();
+	if (!ctx)
+		return 0;
+
+	if (!EVP_DecryptInit_ex(ctx, aead, NULL, NULL, NULL) ||
+	    !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, NULL) ||
+	    (aead_nid == NID_aes_128_ccm &&
+	     !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, QUIC_TLS_TAG_LEN, NULL)) ||
+	    !EVP_DecryptInit_ex(ctx, NULL, NULL, key, NULL))
+		goto err;
+
+	*rx_ctx = ctx;
+
+	return 1;
+
+ err:
+	EVP_CIPHER_CTX_free(ctx);
+	return 0;
+}
+
+/* Initialize the cipher context for TX part of <tls_ctx> QUIC TLS context.
+ * Return 1 if succeeded, 0 if not.
+ */
+int quic_tls_tx_ctx_init(EVP_CIPHER_CTX **tx_ctx,
+                         const EVP_CIPHER *aead, unsigned char *key)
+{
+	EVP_CIPHER_CTX *ctx;
+	int aead_nid = EVP_CIPHER_nid(aead);
+
+	ctx = EVP_CIPHER_CTX_new();
+	if (!ctx)
+		return 0;
+
+	if (!EVP_EncryptInit_ex(ctx, aead, NULL, NULL, NULL) ||
+	    !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, NULL) ||
+	    (aead_nid == NID_aes_128_ccm &&
+	     !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, QUIC_TLS_TAG_LEN, NULL)) ||
+	    !EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL))
+		goto err;
+
+	*tx_ctx = ctx;
+
+	return 1;
+
+ err:
+	EVP_CIPHER_CTX_free(ctx);
+	return 0;
+}
+
 /*
  * https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#aead
  *
@@ -335,66 +393,63 @@
  * the AEAD that is in use.
  */
 
+/* Encrypt in place <buf> plaintext with <len> as length with QUIC_TLS_TAG_LEN
+ * included tailing bytes for the tag.
+ * Note that for CCM mode, we must set the the ciphertext length if AAD data
+ * are provided from <aad> buffer with <aad_len> as length. This is always the
+ * case here. So the caller of this function must provide <aad>.
+ *
+ * https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption
+ */
 int quic_tls_encrypt(unsigned char *buf, size_t len,
                      const unsigned char *aad, size_t aad_len,
-                     const EVP_CIPHER *aead, const unsigned char *key, const unsigned char *iv)
+                     EVP_CIPHER_CTX *ctx, const EVP_CIPHER *aead,
+                     const unsigned char *key, const unsigned char *iv)
 {
-	EVP_CIPHER_CTX *ctx;
-	int ret, outlen;
-
-	ret = 0;
-	ctx = EVP_CIPHER_CTX_new();
-	if (!ctx)
-		return 0;
+	int outlen;
+	int aead_nid = EVP_CIPHER_nid(aead);
 
-	if (!EVP_EncryptInit_ex(ctx, aead, NULL, key, iv) ||
+	if (!EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv) ||
+	    (aead_nid == NID_aes_128_ccm &&
+	     !EVP_EncryptUpdate(ctx, NULL, &outlen, NULL, len)) ||
 		!EVP_EncryptUpdate(ctx, NULL, &outlen, aad, aad_len) ||
 		!EVP_EncryptUpdate(ctx, buf, &outlen, buf, len) ||
 		!EVP_EncryptFinal_ex(ctx, buf + outlen, &outlen) ||
 		!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, QUIC_TLS_TAG_LEN, buf + len))
-		goto out;
-
-	ret = 1;
-
- out:
-	EVP_CIPHER_CTX_free(ctx);
+		return 0;
 
-	return ret;
+	return 1;
 }
 
+/* Decrypt in place <buf> ciphertext with <len> as length with QUIC_TLS_TAG_LEN
+ * included tailing bytes for the tag.
+ * Note that for CCM mode, we must set the the ciphertext length if AAD data
+ * are provided from <aad> buffer with <aad_len> as length. This is always the
+ * case here. So the caller of this function must provide <aad>. Also not the
+ * there is no need to call EVP_DecryptFinal_ex for CCM mode.
+ *
+ * https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption
+ */
 int quic_tls_decrypt(unsigned char *buf, size_t len,
                      unsigned char *aad, size_t aad_len,
-                     const EVP_CIPHER *aead, const unsigned char *key, const unsigned char *iv)
+                     EVP_CIPHER_CTX *ctx, const EVP_CIPHER *aead,
+                     const unsigned char *key, const unsigned char *iv)
 {
-	int ret, outlen;
-	size_t off;
-	EVP_CIPHER_CTX *ctx;
-
-	ret = 0;
-	off = 0;
-	ctx = EVP_CIPHER_CTX_new();
-	if (!ctx)
-		return 0;
-
-	if (!EVP_DecryptInit_ex(ctx, aead, NULL, key, iv) ||
-		!EVP_DecryptUpdate(ctx, NULL, &outlen, aad, aad_len) ||
-		!EVP_DecryptUpdate(ctx, buf, &outlen, buf, len - QUIC_TLS_TAG_LEN))
-		goto out;
+	int outlen;
+	int aead_nid = EVP_CIPHER_nid(aead);
 
-	off += outlen;
-
-	if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, QUIC_TLS_TAG_LEN,
+	if (!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, iv) ||
+	    !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, QUIC_TLS_TAG_LEN,
 	                         buf + len - QUIC_TLS_TAG_LEN) ||
-	    !EVP_DecryptFinal_ex(ctx, buf + off, &outlen))
-		goto out;
-
-	off += outlen;
-
-	ret = off;
+	    (aead_nid == NID_aes_128_ccm &&
+	     !EVP_DecryptUpdate(ctx, NULL, &outlen, NULL, len - QUIC_TLS_TAG_LEN)) ||
+		!EVP_DecryptUpdate(ctx, NULL, &outlen, aad, aad_len) ||
+		!EVP_DecryptUpdate(ctx, buf, &outlen, buf, len - QUIC_TLS_TAG_LEN) ||
+		(aead_nid != NID_aes_128_ccm &&
+		 !EVP_DecryptFinal_ex(ctx, buf + outlen, &outlen)))
+		return 0;
 
- out:
-	EVP_CIPHER_CTX_free(ctx);
-	return ret;
+	return 1;
 }
 
 /* Generate the AEAD tag for the Retry packet <pkt> of <pkt_len> bytes and
diff --git a/src/xprt_quic.c b/src/xprt_quic.c
index d3ab707..01bf9e0 100644
--- a/src/xprt_quic.c
+++ b/src/xprt_quic.c
@@ -809,6 +809,11 @@
 		goto err;
 	}
 
+	if (!quic_tls_rx_ctx_init(&rx->ctx, rx->aead, rx->key)) {
+		TRACE_DEVEL("could not initial RX TLS cipher context", QUIC_EV_CONN_RWSEC, qc);
+		goto err;
+	}
+
 	/* Enqueue this connection asap if we could derive O-RTT secrets as
 	 * listener. Note that a listener derives only RX secrets for this
 	 * level.
@@ -826,6 +831,11 @@
 		goto err;
 	}
 
+	if (!quic_tls_tx_ctx_init(&tx->ctx, tx->aead, tx->key)) {
+		TRACE_DEVEL("could not initial RX TLS cipher context", QUIC_EV_CONN_RWSEC, qc);
+		goto err;
+	}
+
 	if (level == ssl_encryption_application) {
 		struct quic_tls_kp *prv_rx = &qc->ku.prv_rx;
 		struct quic_tls_kp *nxt_rx = &qc->ku.nxt_rx;
@@ -1335,7 +1345,7 @@
 	}
 
 	if (!quic_tls_encrypt(payload, payload_len, aad, aad_len,
-	                      tls_ctx->tx.aead, tls_ctx->tx.key, iv)) {
+	                      tls_ctx->tx.ctx, tls_ctx->tx.aead, tls_ctx->tx.key, iv)) {
 		TRACE_DEVEL("QUIC packet encryption failed", QUIC_EV_CONN_HPKT, qc);
 		goto err;
 	}
@@ -1391,7 +1401,7 @@
 
 	ret = quic_tls_decrypt(pkt->data + pkt->aad_len, pkt->len - pkt->aad_len,
 	                       pkt->data, pkt->aad_len,
-	                       tls_ctx->rx.aead, rx_key, iv);
+	                       tls_ctx->rx.ctx, tls_ctx->rx.aead, rx_key, iv);
 	if (!ret)
 		return 0;
 
@@ -1408,7 +1418,7 @@
 	}
 
 	/* Update the packet length (required to parse the frames). */
-	pkt->len = pkt->aad_len + ret;
+	pkt->len -= QUIC_TLS_TAG_LEN;
 
 	return 1;
 }