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;
}