From: Frédéric Lécaille Date: Tue, 5 Apr 2022 08:28:29 +0000 (+0200) Subject: MINOR: quic_tls: Add reusable cipher contexts to QUIC TLS contexts X-Git-Tag: v2.6-dev5~22 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f4605748f44dac766e910138813b76a0be9f3635;p=thirdparty%2Fhaproxy.git 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 7dab78d0f1..59a8186e4f 100644 --- a/include/haproxy/quic_tls-t.h +++ b/include/haproxy/quic_tls-t.h @@ -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; diff --git a/include/haproxy/quic_tls.h b/include/haproxy/quic_tls.h index b4a6675bdb..d12d6e07b7 100644 --- a/include/haproxy/quic_tls.h +++ b/include/haproxy/quic_tls.h @@ -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); diff --git a/src/quic_tls.c b/src/quic_tls.c index d4314d7cb4..cff461c8da 100644 --- a/src/quic_tls.c +++ b/src/quic_tls.c @@ -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 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 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 plaintext with 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 buffer with as length. This is always the + * case here. So the caller of this function must provide . + * + * 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 ciphertext with 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 buffer with as length. This is always the + * case here. So the caller of this function must provide . 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 of bytes and diff --git a/src/xprt_quic.c b/src/xprt_quic.c index d3ab7076e5..01bf9e0553 100644 --- a/src/xprt_quic.c +++ b/src/xprt_quic.c @@ -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; }