]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: quic_tls: Add reusable cipher contexts to QUIC TLS contexts
authorFrédéric Lécaille <flecaille@haproxy.com>
Tue, 5 Apr 2022 08:28:29 +0000 (10:28 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 8 Apr 2022 13:38:29 +0000 (15:38 +0200)
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.

include/haproxy/quic_tls-t.h
include/haproxy/quic_tls.h
src/quic_tls.c
src/xprt_quic.c

index 7dab78d0f18dfc06346370ae7d2d7720768f8537..59a8186e4f646976f1945dc08288888a140aa750 100644 (file)
@@ -112,6 +112,7 @@ struct quic_tls_kp {
 #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;
index b4a6675bdb2b0f7d3ee43c0e191b9326c8b40a30..d12d6e07b73af7f88650d3f8de954e4f53ca0ea8 100644 (file)
@@ -61,12 +61,12 @@ int quic_tls_derive_initial_secrets(const EVP_MD *md,
 
 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 @@ int quic_tls_derive_keys(const EVP_CIPHER *aead, const EVP_CIPHER *hp,
                          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 @@ static inline void quic_tls_ctx_secs_free(struct quic_tls_ctx *ctx)
                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 @@ static inline int qc_new_isecs(struct quic_conn *qc,
                                  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 @@ static inline int qc_new_isecs(struct quic_conn *qc,
                                  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);
 
index d4314d7cb4ca6fff6dd2051e10536872c30ecf7e..cff461c8dac3b28c45530b3609315bfc44dc284c 100644 (file)
@@ -303,6 +303,64 @@ int quic_aead_iv_build(unsigned char *iv, size_t ivlen,
        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 @@ int quic_aead_iv_build(unsigned char *iv, size_t ivlen,
  * 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;
+       int outlen;
+       int aead_nid = EVP_CIPHER_nid(aead);
 
-       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;
-
-       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
index d3ab7076e5b505f7bad6534cfe534ea85b912aa6..01bf9e05536ce9db196d7d552a0ffe8e822da2ec 100644 (file)
@@ -809,6 +809,11 @@ int ha_quic_set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t level,
                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 @@ int ha_quic_set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t level,
                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 @@ static int quic_packet_encrypt(unsigned char *payload, size_t payload_len,
        }
 
        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 @@ static int qc_pkt_decrypt(struct quic_rx_packet *pkt, struct quic_enc_level *qel
 
        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 @@ static int qc_pkt_decrypt(struct quic_rx_packet *pkt, struct quic_enc_level *qel
        }
 
        /* Update the packet length (required to parse the frames). */
-       pkt->len = pkt->aad_len + ret;
+       pkt->len -= QUIC_TLS_TAG_LEN;
 
        return 1;
 }