]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: quic: Dynamic allocations of QUIC TLS encryption levels
authorFrédéric Lécaille <flecaille@haproxy.com>
Thu, 22 Jun 2023 05:19:03 +0000 (07:19 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 30 Jun 2023 14:20:55 +0000 (16:20 +0200)
Replace ->els static array of encryption levels by 4 pointers into the QUIC
connection object, quic_conn struct.
    ->iel denotes the Initial encryption level,
    ->eel the Early-Data encryption level,
    ->hel the Handshaske encryption level and
    ->ael the Application Data encryption level.

Add ->qel_list to this structure to list the encryption levels after having been
allocated. Modify consequently the encryption level object itself (quic_enc_level
struct) so that it might be added to ->qel_list QUIC connection list of
encryption levels.

Implement qc_enc_level_alloc() to initialize the value of a pointer to an encryption
level object. It is used to initialized the pointer newly added to the quic_conn
structure. It also takes a packet number space pointer address as argument to
initialize it if not already initialized.

Modify quic_tls_ctx_reset() to call it from quic_conn_enc_level_init() which is
called by qc_enc_level_alloc() to allocate an encryption level object.

Implement 2 new helper functions:
  - ssl_to_qel_addr() to match and pointer address to a quic_encryption level
    attached to a quic_conn object with a TLS encryption level enum value;
  - qc_quic_enc_level() to match a pointer to a quic_encryption level attached
    to a quic_conn object with an internal encryption level enum value.
This functions are useful to be called from ->set_encryption_secrets() and
->add_handshake_data() TLS stack called which takes a TLS encryption enum
as argument (enum ssl_encryption_level_t).

Replace all the use of the qc->els[] array element values by one of the newly
added ->[ieha]el quic_conn struct member values.

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

index 3e1435aa8e9508404bee506ae4441dae9fe3cf2f..eb05fd5376c3a407e24f4fa7b24eaa9fbfa525d5 100644 (file)
@@ -597,7 +597,17 @@ struct quic_conn {
        struct eb_root cids; /* tree of quic_connection_id - used to match a received packet DCID with a connection */
        uint64_t next_cid_seq_num;
 
-       struct quic_enc_level els[QUIC_TLS_ENC_LEVEL_MAX];
+       /* Initial encryption level */
+       struct quic_enc_level *iel;
+       /* 0-RTT encryption level */
+       struct quic_enc_level *eel;
+       /* Handshake encryption level */
+       struct quic_enc_level *hel;
+       /* 1-RTT encryption level */
+       struct quic_enc_level *ael;
+       /* List of allocated QUIC TLS encryption level */
+       struct list qel_list;
+
        struct quic_pktns *ipktns;
        struct quic_pktns *hpktns;
        struct quic_pktns *apktns;
index 6f259900d8679ebc6eb81629fd288564921ea6dd..c48b043a7e0b81e2f8fffa00cf011d4b5779b358 100644 (file)
@@ -54,6 +54,7 @@ int ssl_quic_initial_ctx(struct bind_conf *bind_conf);
 struct quic_cstream *quic_cstream_new(struct quic_conn *qc);
 void quic_cstream_free(struct quic_cstream *cs);
 void quic_free_arngs(struct quic_conn *qc, struct quic_arngs *arngs);
+struct quic_cstream *quic_cstream_new(struct quic_conn *qc);
 
 /* Return the long packet type matching with <qv> version and <type> */
 static inline int quic_pkt_type(int type, uint32_t version)
index b871c904f2664ebf7c2651a2b025a157bbeb90b7..71f8932fdb738f4c5fab8389d316b78fe1571334 100644 (file)
@@ -217,6 +217,7 @@ struct quic_tls_ctx {
 };
 
 struct quic_enc_level {
+       struct list list;
        /* Encryption level, as defined by the TLS stack. */
        enum ssl_encryption_level_t level;
        /* TLS encryption context (AEAD only) */
index a7bebc668c3603b7a69502cc2febc626c1fdc1b9..0c6aff9bed950c7c9e7b2c71ff1a9d49d6af18a7 100644 (file)
 #include <haproxy/trace.h>
 
 void quic_pktns_release(struct quic_conn *qc, struct quic_pktns **pktns);
+int qc_enc_level_alloc(struct quic_conn *qc, struct quic_pktns **pktns,
+                       struct quic_enc_level **qel, enum ssl_encryption_level_t level);
+void qc_enc_level_free(struct quic_conn *qc, struct quic_enc_level **qel);
+
 void quic_tls_keys_hexdump(struct buffer *buf,
                            const struct quic_tls_secrets *secs);
 void quic_tls_kp_keys_hexdump(struct buffer *buf,
                               const struct quic_tls_kp *kp);
 
-int quic_conn_enc_level_init(struct quic_conn *qc,
-                             enum quic_tls_enc_level level);
 void quic_conn_enc_level_uninit(struct quic_conn *qc, struct quic_enc_level *qel);
 void quic_tls_secret_hexdump(struct buffer *buf,
                              const unsigned char *secret, size_t secret_len);
@@ -181,7 +183,10 @@ static inline struct quic_pktns **ssl_to_quic_pktns(struct quic_conn *qc,
                return NULL;
        }
 }
-/* These following functions map TLS implementation encryption level to ours */
+
+/* Map <level> TLS stack encryption level to our internal QUIC TLS encryption level
+ * if succeded, or -1 if failed.
+ */
 static inline enum quic_tls_enc_level ssl_to_quic_enc_level(enum ssl_encryption_level_t level)
 {
        switch (level) {
@@ -198,6 +203,47 @@ static inline enum quic_tls_enc_level ssl_to_quic_enc_level(enum ssl_encryption_
        }
 }
 
+/* Return the address of the QUIC TLS encryption level associated to <level> TLS
+ * stack encryption level and attached to <qc> QUIC connection if succeeded, or
+ * NULL if failed.
+ */
+static inline struct quic_enc_level **ssl_to_qel_addr(struct quic_conn *qc,
+                                                      enum ssl_encryption_level_t level)
+{
+       switch (level) {
+       case ssl_encryption_initial:
+               return &qc->iel;
+       case ssl_encryption_early_data:
+               return &qc->eel;
+       case ssl_encryption_handshake:
+               return &qc->hel;
+       case ssl_encryption_application:
+               return &qc->ael;
+       default:
+               return NULL;
+       }
+}
+
+/* Return the QUIC TLS encryption level associated to <level> internal encryption
+ * level attached to <qc> QUIC connection if succeeded, or NULL if failed.
+ */
+static inline struct quic_enc_level *qc_quic_enc_level(const struct quic_conn *qc,
+                                                       enum quic_tls_enc_level level)
+{
+       switch (level) {
+       case QUIC_TLS_ENC_LEVEL_INITIAL:
+               return qc->iel;
+       case QUIC_TLS_ENC_LEVEL_EARLY_DATA:
+               return qc->eel;
+       case QUIC_TLS_ENC_LEVEL_HANDSHAKE:
+               return qc->hel;
+       case QUIC_TLS_ENC_LEVEL_APP:
+               return qc->ael;
+       default:
+               return NULL;
+       }
+}
+
 /* These two following functions map our encryption level to the TLS implementation ones. */
 static inline enum ssl_encryption_level_t quic_to_ssl_enc_level(enum quic_tls_enc_level level)
 {
@@ -309,13 +355,13 @@ static inline char quic_enc_level_char(enum quic_tls_enc_level level)
 static inline char quic_enc_level_char_from_qel(const struct quic_enc_level *qel,
                                                 const struct quic_conn *qc)
 {
-       if (qel == &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL])
+       if (qel == qc->iel)
                return 'I';
-       else if (qel == &qc->els[QUIC_TLS_ENC_LEVEL_EARLY_DATA])
+       else if (qel == qc->eel)
                return 'E';
-       else if (qel == &qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE])
+       else if (qel == qc->hel)
                return 'H';
-       else if (qel == &qc->els[QUIC_TLS_ENC_LEVEL_APP])
+       else if (qel == qc->ael)
                return 'A';
        return '-';
 }
@@ -499,17 +545,25 @@ static inline enum quic_tls_pktns quic_tls_pktns(enum quic_tls_enc_level level)
        }
 }
 
-/* Reset all members of <ctx> to default values. */
+/* Reset all members of <ctx> to default values, ->hp_key[] excepted */
 static inline void quic_tls_ctx_reset(struct quic_tls_ctx *ctx)
 {
        ctx->rx.ctx = NULL;
+       ctx->rx.aead = NULL;
+       ctx->rx.md = NULL;
        ctx->rx.hp_ctx = NULL;
+       ctx->rx.hp = NULL;
+       ctx->rx.secret = NULL;
        ctx->rx.iv = NULL;
        ctx->rx.key = NULL;
        ctx->rx.pn = 0;
 
        ctx->tx.ctx = NULL;
+       ctx->tx.aead = NULL;
+       ctx->tx.md = NULL;
        ctx->tx.hp_ctx = NULL;
+       ctx->tx.hp = NULL;
+       ctx->tx.secret = NULL;
        ctx->tx.iv = NULL;
        ctx->tx.key = NULL;
        /* Not used on the TX path. */
@@ -850,13 +904,13 @@ static inline int quic_tls_ku_init(struct quic_conn *qc)
 /* Return 1 if <qel> has RX secrets, 0 if not. */
 static inline int quic_tls_has_rx_sec(const struct quic_enc_level *qel)
 {
-       return !!qel->tls_ctx.rx.key;
+       return qel && !!qel->tls_ctx.rx.key;
 }
 
 /* Return 1 if <qel> has TX secrets, 0 if not. */
 static inline int quic_tls_has_tx_sec(const struct quic_enc_level *qel)
 {
-       return !!qel->tls_ctx.tx.key;
+       return qel && !!qel->tls_ctx.tx.key;
 }
 
 #endif /* USE_QUIC */
index a1d65508527d08a121932b2822a1ea9a486f3ab1..f44945af21280e1396d6df234a69b8597c4939ee 100644 (file)
@@ -227,9 +227,6 @@ DECLARE_STATIC_POOL(pool_head_quic_cstream, "quic_cstream", sizeof(struct quic_c
 DECLARE_POOL(pool_head_quic_frame, "quic_frame", sizeof(struct quic_frame));
 DECLARE_STATIC_POOL(pool_head_quic_arng, "quic_arng", sizeof(struct quic_arng_node));
 
-static int quic_conn_enc_level_init(struct quic_conn *qc,
-                                    struct quic_pktns *pktns,
-                                    enum quic_tls_enc_level level);
 static struct quic_connection_id *new_quic_cid(struct eb_root *root,
                                                struct quic_conn *qc,
                                                const struct quic_cid *odcid,
@@ -320,16 +317,15 @@ static void quic_trace(enum trace_level level, uint64_t mask, const struct trace
                }
                if ((mask & QUIC_EV_CONN_ISEC) && qc) {
                        /* Initial read & write secrets. */
-                       enum quic_tls_enc_level level = QUIC_TLS_ENC_LEVEL_INITIAL;
                        const unsigned char *rx_sec = a2;
                        const unsigned char *tx_sec = a3;
 
-                       tls_ctx = &qc->els[level].tls_ctx;
-                       chunk_appendf(&trace_buf, "\n  RX el=%c", quic_enc_level_char(level));
+                       tls_ctx = &qc->iel->tls_ctx;
+                       chunk_appendf(&trace_buf, "\n  RX el=I");
                        if (rx_sec)
                                quic_tls_secret_hexdump(&trace_buf, rx_sec, 32);
                        quic_tls_keys_hexdump(&trace_buf, &tls_ctx->rx);
-                       chunk_appendf(&trace_buf, "\n  TX el=%c", quic_enc_level_char(level));
+                       chunk_appendf(&trace_buf, "\n  TX el=I");
                        if (tx_sec)
                                quic_tls_secret_hexdump(&trace_buf, tx_sec, 32);
                        quic_tls_keys_hexdump(&trace_buf, &tls_ctx->tx);
@@ -360,12 +356,11 @@ static void quic_trace(enum trace_level level, uint64_t mask, const struct trace
 
                        if (level) {
                                enum quic_tls_enc_level lvl = ssl_to_quic_enc_level(*level);
+                               struct quic_enc_level *qel = qc_quic_enc_level(qc, lvl);
 
                                chunk_appendf(&trace_buf, "\n  RX el=%c", quic_enc_level_char(lvl));
-                               if (quic_tls_has_rx_sec(&qc->els[lvl])) {
-                                       tls_ctx = &qc->els[lvl].tls_ctx;
-                                       quic_tls_keys_hexdump(&trace_buf, &tls_ctx->rx);
-                               }
+                               if (quic_tls_has_rx_sec(qel))
+                                       quic_tls_keys_hexdump(&trace_buf, &qel->tls_ctx.rx);
                                else
                                        chunk_appendf(&trace_buf, " (none)");
                        }
@@ -376,11 +371,11 @@ static void quic_trace(enum trace_level level, uint64_t mask, const struct trace
 
                        if (level) {
                                enum quic_tls_enc_level lvl = ssl_to_quic_enc_level(*level);
+                               struct quic_enc_level *qel = qc_quic_enc_level(qc, lvl);
 
                                chunk_appendf(&trace_buf, "\n  TX el=%c", quic_enc_level_char(lvl));
-                               if (quic_tls_has_tx_sec(&qc->els[lvl])) {
-                                       tls_ctx = &qc->els[lvl].tls_ctx;
-                                       quic_tls_keys_hexdump(&trace_buf, &tls_ctx->tx);
+                               if (quic_tls_has_tx_sec(qel)) {
+                                       quic_tls_keys_hexdump(&trace_buf, &qel->tls_ctx.tx);
                                }
                                else
                                        chunk_appendf(&trace_buf, " (none)");
@@ -908,7 +903,7 @@ static inline void qc_set_timer(struct quic_conn *qc)
  */
 static int quic_tls_key_update(struct quic_conn *qc)
 {
-       struct quic_tls_ctx *tls_ctx = &qc->els[QUIC_TLS_ENC_LEVEL_APP].tls_ctx;
+       struct quic_tls_ctx *tls_ctx = &qc->ael->tls_ctx;
        struct quic_tls_secrets *rx = &tls_ctx->rx;
        struct quic_tls_secrets *tx = &tls_ctx->tx;
        /* Used only for the traces */
@@ -998,7 +993,7 @@ static int quic_tls_key_update(struct quic_conn *qc)
  */
 static void quic_tls_rotate_keys(struct quic_conn *qc)
 {
-       struct quic_tls_ctx *tls_ctx = &qc->els[QUIC_TLS_ENC_LEVEL_APP].tls_ctx;
+       struct quic_tls_ctx *tls_ctx = &qc->ael->tls_ctx;
        unsigned char *curr_secret, *curr_iv, *curr_key;
        EVP_CIPHER_CTX *curr_ctx;
 
@@ -1050,30 +1045,26 @@ int ha_quic_set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t level,
                                    const uint8_t *read_secret,
                                    const uint8_t *write_secret, size_t secret_len)
 {
+       int ret = 0;
        struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
-       enum quic_tls_enc_level tel = ssl_to_quic_enc_level(level);
-       struct quic_enc_level *qel = &qc->els[tel];
-       struct quic_tls_ctx *tls_ctx = &qel->tls_ctx;
+       struct quic_enc_level **qel = ssl_to_qel_addr(qc, level);
+       struct quic_pktns **pktns = ssl_to_quic_pktns(qc, level);
+       struct quic_tls_ctx *tls_ctx;
        const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
        struct quic_tls_secrets *rx = NULL, *tx = NULL;
        const struct quic_version *ver =
                qc->negotiated_version ? qc->negotiated_version : qc->original_version;
-       int ret = 0;
 
        TRACE_ENTER(QUIC_EV_CONN_RWSEC, qc);
        BUG_ON(secret_len > QUIC_TLS_SECRET_LEN);
 
-       if (!qel->pktns) {
-               struct quic_pktns **pktns = ssl_to_quic_pktns(qc, level);
-
-               if (pktns == NULL ||
-                   !quic_pktns_init(qc, pktns) ||
-                   !quic_conn_enc_level_init(qc, *pktns, tel)) {
-                       TRACE_ERROR("Could not initialized the packet number space", QUIC_EV_CONN_ADDDATA, qc);
-                       goto leave;
-               }
+       if (!*qel && !qc_enc_level_alloc(qc, pktns, qel, level)) {
+               TRACE_PROTO("Could not allocated an encryption level", QUIC_EV_CONN_ADDDATA, qc);
+               goto out;
        }
 
+       tls_ctx = &(*qel)->tls_ctx;
+
        if (qc->flags & QUIC_FL_CONN_TO_KILL) {
                TRACE_PROTO("connection to be killed", QUIC_EV_CONN_ADDDATA, qc);
                goto out;
@@ -1366,12 +1357,11 @@ int quic_set_app_ops(struct quic_conn *qc, const unsigned char *alpn, size_t alp
 int ha_quic_add_handshake_data(SSL *ssl, enum ssl_encryption_level_t level,
                                const uint8_t *data, size_t len)
 {
-       struct quic_conn *qc;
-       enum quic_tls_enc_level tel;
-       struct quic_enc_level *qel;
        int ret = 0;
+       struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
+       struct quic_enc_level **qel = ssl_to_qel_addr(qc, level);
+       struct quic_pktns **pktns = ssl_to_quic_pktns(qc, level);
 
-       qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
        TRACE_ENTER(QUIC_EV_CONN_ADDDATA, qc);
 
        if (qc->flags & QUIC_FL_CONN_TO_KILL) {
@@ -1384,25 +1374,10 @@ int ha_quic_add_handshake_data(SSL *ssl, enum ssl_encryption_level_t level,
                goto out;
        }
 
-       tel = ssl_to_quic_enc_level(level);
-       if (tel == -1) {
-               TRACE_ERROR("Wrong encryption level", QUIC_EV_CONN_ADDDATA, qc);
+       if (!*qel && !qc_enc_level_alloc(qc, pktns, qel, level))
                goto leave;
-       }
 
-       qel = &qc->els[tel];
-       if (!qel->pktns) {
-               struct quic_pktns **pktns = ssl_to_quic_pktns(qc, level);
-
-               if (pktns == NULL ||
-                   !quic_pktns_init(qc, pktns) ||
-                   !quic_conn_enc_level_init(qc, *pktns, tel)) {
-                       TRACE_ERROR("Could not initialized the packet number space", QUIC_EV_CONN_ADDDATA, qc);
-                       goto leave;
-               }
-       }
-
-       if (!quic_crypto_data_cpy(qc, qel, data, len)) {
+       if (!quic_crypto_data_cpy(qc, *qel, data, len)) {
                TRACE_ERROR("Could not bufferize", QUIC_EV_CONN_ADDDATA, qc);
                goto leave;
        }
@@ -2767,8 +2742,8 @@ static void qc_prep_hdshk_fast_retrans(struct quic_conn *qc,
        struct list itmp = LIST_HEAD_INIT(itmp);
        struct list htmp = LIST_HEAD_INIT(htmp);
 
-       struct quic_enc_level *iqel = &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL];
-       struct quic_enc_level *hqel = &qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE];
+       struct quic_enc_level *iqel = qc->iel;
+       struct quic_enc_level *hqel = qc->hel;
        struct quic_enc_level *qel = iqel;
        struct eb_root *pkts;
        struct eb64_node *node;
@@ -2869,7 +2844,7 @@ static int qc_h3_request_reject(struct quic_conn *qc, uint64_t id)
 {
        int ret = 0;
        struct quic_frame *ss, *rs;
-       struct quic_enc_level *qel = &qc->els[QUIC_TLS_ENC_LEVEL_APP];
+       struct quic_enc_level *qel = qc->ael;
        const uint64_t app_error_code = H3_REQUEST_REJECTED;
 
        TRACE_ENTER(QUIC_EV_CONN_PRSHPKT, qc);
@@ -2976,7 +2951,7 @@ static int qc_handle_crypto_frm(struct quic_conn *qc,
                        /* Nothing to do */
                        TRACE_PROTO("Already received CRYPTO data",
                                                QUIC_EV_CONN_RXPKT, qc, pkt, &cfdebug);
-                       if (qc_is_listener(qc) && qel == &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL] &&
+                       if (qc_is_listener(qc) && qel == qc->iel &&
                                !(qc->flags & QUIC_FL_CONN_HANDSHAKE_SPEED_UP))
                                *fast_retrans = 1;
                        goto done;
@@ -3045,7 +3020,7 @@ static int qc_build_new_connection_id_frm(struct quic_conn *qc,
 
        TRACE_ENTER(QUIC_EV_CONN_PRSHPKT, qc);
 
-       qel = &qc->els[QUIC_TLS_ENC_LEVEL_APP];
+       qel = qc->ael;
        frm = qc_frm_alloc(QUIC_FT_NEW_CONNECTION_ID);
        if (!frm) {
                TRACE_ERROR("frame allocation error", QUIC_EV_CONN_IO_CB, qc);
@@ -3361,8 +3336,8 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt,
        qel->pktns->flags |= QUIC_FL_PKTNS_PKT_RECEIVED;
 
        if (fast_retrans) {
-               struct quic_enc_level *iqel = &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL];
-               struct quic_enc_level *hqel = &qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE];
+               struct quic_enc_level *iqel = qc->iel;
+               struct quic_enc_level *hqel = qc->hel;
 
                TRACE_PROTO("speeding up handshake completion", QUIC_EV_CONN_PRSHPKT, qc);
                qc_prep_hdshk_fast_retrans(qc, &iqel->pktns->tx.frms, &hqel->pktns->tx.frms);
@@ -3375,14 +3350,14 @@ static int qc_parse_pkt_frms(struct quic_conn *qc, struct quic_rx_packet *pkt,
         */
        if (pkt->type == QUIC_PACKET_TYPE_HANDSHAKE && qc_is_listener(qc)) {
            if (qc->state >= QUIC_HS_ST_SERVER_INITIAL) {
-                       if (!(qc->els[QUIC_TLS_ENC_LEVEL_INITIAL].tls_ctx.flags &
+                       if (!(qc->iel->tls_ctx.flags &
                              QUIC_FL_TLS_SECRETS_DCD)) {
-                               quic_tls_discard_keys(&qc->els[QUIC_TLS_ENC_LEVEL_INITIAL]);
+                               quic_tls_discard_keys(qc->iel);
                                TRACE_PROTO("discarding Initial pktns", QUIC_EV_CONN_PRSHPKT, qc);
-                               quic_pktns_discard(qc->els[QUIC_TLS_ENC_LEVEL_INITIAL].pktns, qc);
+                               quic_pktns_discard(qc->iel->pktns, qc);
                                qc_set_timer(qc);
-                               qc_el_rx_pkts_del(&qc->els[QUIC_TLS_ENC_LEVEL_INITIAL]);
-                               qc_release_pktns_frms(qc, qc->els[QUIC_TLS_ENC_LEVEL_INITIAL].pktns);
+                               qc_el_rx_pkts_del(qc->iel);
+                               qc_release_pktns_frms(qc, qc->iel->pktns);
                        }
                    if (qc->state < QUIC_HS_ST_SERVER_HANDSHAKE)
                            qc->state = QUIC_HS_ST_SERVER_HANDSHAKE;
@@ -3454,9 +3429,7 @@ static int qc_may_build_pkt(struct quic_conn *qc, struct list *frms,
                             struct quic_enc_level *qel, int cc, int probe,
                             int *must_ack)
 {
-       int force_ack =
-               qel == &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL] ||
-               qel == &qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE];
+       int force_ack = qel == qc->iel || qel == qc->hel;
        int nb_aepkts_since_last_ack = qel->pktns->rx.nb_aepkts_since_last_ack;
 
        /* An acknowledgement must be sent if this has been forced by the caller,
@@ -3507,7 +3480,7 @@ static int qc_prep_app_pkts(struct quic_conn *qc, struct buffer *buf,
 
        TRACE_ENTER(QUIC_EV_CONN_PHPKTS, qc);
 
-       qel = &qc->els[QUIC_TLS_ENC_LEVEL_APP];
+       qel = qc->ael;
        total = 0;
        pos = (unsigned char *)b_tail(buf);
        while (b_contig_space(buf) >= (int)qc->path->mtu + dg_headlen) {
@@ -3603,7 +3576,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
        BUG_ON_HOT(buf->head || buf->data);
 
        total = 0;
-       qel = &qc->els[tel];
+       qel = qc_quic_enc_level(qc, tel);
        frms = tel_frms;
        dglen = 0;
        padding = 0;
@@ -3629,7 +3602,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
                        if (tel != next_tel && next_tel != QUIC_TLS_ENC_LEVEL_NONE) {
                                tel = next_tel;
                                frms = next_tel_frms;
-                               qel = &qc->els[tel];
+                               qel = qc_quic_enc_level(qc, tel);
                                /* Build a new datagram */
                                prv_pkt = NULL;
                                TRACE_DEVEL("next encryption level selected", QUIC_EV_CONN_PHPKTS, qc);
@@ -3673,7 +3646,7 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
 
                if (qc->negotiated_version) {
                        ver = qc->negotiated_version;
-                       if (qel == &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL])
+                       if (qel == qc->iel)
                                tls_ctx = &qc->negotiated_ictx;
                        else
                                tls_ctx = &qel->tls_ctx;
@@ -3726,12 +3699,12 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
                 */
                if (qc->state == QUIC_HS_ST_CLIENT_INITIAL &&
                    pkt_type == QUIC_PACKET_TYPE_HANDSHAKE) {
-                       quic_tls_discard_keys(&qc->els[QUIC_TLS_ENC_LEVEL_INITIAL]);
+                       quic_tls_discard_keys(qc->iel);
                        TRACE_PROTO("discarding Initial pktns", QUIC_EV_CONN_PHPKTS, qc);
-                       quic_pktns_discard(qc->els[QUIC_TLS_ENC_LEVEL_INITIAL].pktns, qc);
+                       quic_pktns_discard(qc->iel->pktns, qc);
                        qc_set_timer(qc);
-                       qc_el_rx_pkts_del(&qc->els[QUIC_TLS_ENC_LEVEL_INITIAL]);
-                       qc_release_pktns_frms(qc, qc->els[QUIC_TLS_ENC_LEVEL_INITIAL].pktns);
+                       qc_el_rx_pkts_del(qc->iel);
+                       qc_release_pktns_frms(qc, qc->iel->pktns);
                        qc->state = QUIC_HS_ST_CLIENT_HANDSHAKE;
                }
                /* If the data for the current encryption level have all been sent,
@@ -3743,11 +3716,11 @@ static int qc_prep_pkts(struct quic_conn *qc, struct buffer *buf,
                        if (tel == QUIC_TLS_ENC_LEVEL_HANDSHAKE && next_tel == tel)
                                next_tel = QUIC_TLS_ENC_LEVEL_APP;
                        tel = next_tel;
+                       qel = qc_quic_enc_level(qc, tel);
                        if (tel == QUIC_TLS_ENC_LEVEL_APP)
-                               frms = &qc->els[tel].pktns->tx.frms;
+                               frms = &qel->pktns->tx.frms;
                        else
                                frms = next_tel_frms;
-                       qel = &qc->els[tel];
                        if (!LIST_ISEMPTY(frms)) {
                                /* If there is data for the next level, do not
                                 * consume a datagram.
@@ -4188,7 +4161,7 @@ static int quic_build_post_handshake_frames(struct quic_conn *qc)
 
        TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
 
-       qel = &qc->els[QUIC_TLS_ENC_LEVEL_APP];
+       qel = qc->ael;
        /* Only servers must send a HANDSHAKE_DONE frame. */
        if (qc_is_listener(qc)) {
                frm = qc_frm_alloc(QUIC_FT_HANDSHAKE_DONE);
@@ -4473,7 +4446,7 @@ static inline void qc_handle_spin_bit(struct quic_conn *qc, struct quic_rx_packe
 {
        uint64_t largest_pn = qel->pktns->rx.largest_pn;
 
-       if (qel != &qc->els[QUIC_TLS_ENC_LEVEL_APP] || largest_pn == -1 ||
+       if (qel != qc->ael || largest_pn == -1 ||
            pkt->pn <= largest_pn)
                return;
 
@@ -4497,12 +4470,10 @@ static inline void qc_handle_spin_bit(struct quic_conn *qc, struct quic_rx_packe
 static inline void qc_rm_hp_pkts(struct quic_conn *qc, struct quic_enc_level *el)
 {
        struct quic_rx_packet *pqpkt, *pkttmp;
-       struct quic_enc_level *app_qel;
 
        TRACE_ENTER(QUIC_EV_CONN_ELRMHP, qc);
-       app_qel = &qc->els[QUIC_TLS_ENC_LEVEL_APP];
        /* A server must not process incoming 1-RTT packets before the handshake is complete. */
-       if (el == app_qel && qc_is_listener(qc) && qc->state < QUIC_HS_ST_COMPLETE) {
+       if (el == qc->ael && qc_is_listener(qc) && qc->state < QUIC_HS_ST_COMPLETE) {
                TRACE_PROTO("RX hp not removed (handshake not completed)",
                            QUIC_EV_CONN_ELRMHP, qc);
                goto out;
@@ -4679,15 +4650,12 @@ int qc_treat_rx_pkts(struct quic_conn *qc, struct quic_enc_level *cur_el,
 static int qc_qel_may_rm_hp(struct quic_conn *qc, struct quic_enc_level *qel)
 {
        int ret = 0;
-       enum quic_tls_enc_level tel;
 
        TRACE_ENTER(QUIC_EV_CONN_TRMHP, qc);
 
        if (!qel)
                goto cant_rm_hp;
 
-       tel = ssl_to_quic_enc_level(qel->level);
-
        /* check if tls secrets are available */
        if (qel->tls_ctx.flags & QUIC_FL_TLS_SECRETS_DCD) {
                TRACE_PROTO("Discarded keys", QUIC_EV_CONN_TRMHP, qc);
@@ -4699,13 +4667,13 @@ static int qc_qel_may_rm_hp(struct quic_conn *qc, struct quic_enc_level *qel)
                goto cant_rm_hp;
        }
 
-       if (tel == QUIC_TLS_ENC_LEVEL_APP && qc->state < QUIC_HS_ST_COMPLETE) {
+       if (qel == qc->ael && qc->state < QUIC_HS_ST_COMPLETE) {
                TRACE_PROTO("handshake not complete", QUIC_EV_CONN_TRMHP, qc);
                goto cant_rm_hp;
        }
 
        /* check if the connection layer is ready before using app level */
-       if ((tel == QUIC_TLS_ENC_LEVEL_APP || tel == QUIC_TLS_ENC_LEVEL_EARLY_DATA) &&
+       if ((qel == qc->ael || qel == qc->eel) &&
            qc->mux_state == QC_MUX_NULL) {
                TRACE_PROTO("connection layer not ready", QUIC_EV_CONN_TRMHP, qc);
                goto cant_rm_hp;
@@ -4847,9 +4815,8 @@ int qc_send_mux(struct quic_conn *qc, struct list *frms)
        /* Try to send post handshake frames first unless on 0-RTT. */
        if ((qc->flags & QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS) &&
            qc->state >= QUIC_HS_ST_COMPLETE) {
-               struct quic_enc_level *qel = &qc->els[QUIC_TLS_ENC_LEVEL_APP];
                quic_build_post_handshake_frames(qc);
-               qc_send_app_pkts(qc, &qel->pktns->tx.frms);
+               qc_send_app_pkts(qc, &qc->ael->pktns->tx.frms);
        }
 
        TRACE_STATE("preparing data (from MUX)", QUIC_EV_CONN_TXPKT, qc);
@@ -4925,9 +4892,9 @@ int qc_send_hdshk_pkts(struct quic_conn *qc, int old_data,
 static int qc_dgrams_retransmit(struct quic_conn *qc)
 {
        int ret = 0;
-       struct quic_enc_level *iqel = &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL];
-       struct quic_enc_level *hqel = &qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE];
-       struct quic_enc_level *aqel = &qc->els[QUIC_TLS_ENC_LEVEL_APP];
+       struct quic_enc_level *iqel = qc->iel;
+       struct quic_enc_level *hqel = qc->hel;
+       struct quic_enc_level *aqel = qc->ael;
 
        TRACE_ENTER(QUIC_EV_CONN_TXPKT, qc);
 
@@ -5035,7 +5002,7 @@ struct task *quic_conn_app_io_cb(struct task *t, void *context, unsigned int sta
 
        TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
 
-       qel = &qc->els[QUIC_TLS_ENC_LEVEL_APP];
+       qel = qc->ael;
        TRACE_STATE("connection handshake state", QUIC_EV_CONN_IO_CB, qc, &qc->state);
 
        if (qc_test_fd(qc))
@@ -5097,6 +5064,19 @@ static int qc_need_sending(struct quic_conn *qc, struct quic_enc_level *qel)
               !LIST_ISEMPTY(&qel->pktns->tx.frms);
 }
 
+static int qc_prep_hpkts(struct quic_conn *qc, struct buffer *buf,
+                         struct quic_enc_level *qel, struct quic_enc_level *next_qel)
+{
+       enum quic_tls_enc_level next_tel =
+               next_qel ? ssl_to_quic_enc_level(next_qel->level) : QUIC_TLS_ENC_LEVEL_NONE;
+       struct list *next_frms =
+               next_qel ? &next_qel->pktns->tx.frms : NULL;
+
+       return qc_prep_pkts(qc, buf,
+                           ssl_to_quic_enc_level(qel->level), &qel->pktns->tx.frms,
+                           next_tel, next_frms);
+}
+
 /* QUIC connection packet handler task. */
 struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
 {
@@ -5111,7 +5091,7 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
 
        TRACE_ENTER(QUIC_EV_CONN_IO_CB, qc);
 
-       eqel = &qc->els[QUIC_TLS_ENC_LEVEL_EARLY_DATA];
+       eqel = qc->eel;
        st = qc->state;
        TRACE_PROTO("connection state", QUIC_EV_CONN_IO_CB, qc, &st);
 
@@ -5132,7 +5112,7 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
                qc_rcv_buf(qc);
 
        if (st >= QUIC_HS_ST_COMPLETE &&
-           qc_el_rx_pkts(&qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE])) {
+           qc_el_rx_pkts(qc->hel)) {
                TRACE_DEVEL("remaining Handshake packets", QUIC_EV_CONN_PHPKTS, qc);
                /* There may be remaining Handshake packets to treat and acknowledge. */
                tel = QUIC_TLS_ENC_LEVEL_HANDSHAKE;
@@ -5141,8 +5121,8 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
        else if (!quic_get_tls_enc_levels(&tel, &next_tel, qc, st, zero_rtt))
                goto out;
 
-       qel = &qc->els[tel];
-       next_qel = next_tel == QUIC_TLS_ENC_LEVEL_NONE ? NULL : &qc->els[next_tel];
+       qel = qc_quic_enc_level(qc, tel);
+       next_qel = next_tel == QUIC_TLS_ENC_LEVEL_NONE ? NULL : qc_quic_enc_level(qc, next_tel);
 
  next_level:
        /* Treat packets waiting for header packet protection decryption */
@@ -5174,18 +5154,17 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
 
        st = qc->state;
        if (st >= QUIC_HS_ST_COMPLETE) {
-               if (!(qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE].tls_ctx.flags &
-                          QUIC_FL_TLS_SECRETS_DCD)) {
+               if (!(qc->hel->tls_ctx.flags & QUIC_FL_TLS_SECRETS_DCD)) {
                        /* Discard the Handshake keys. */
-                       quic_tls_discard_keys(&qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE]);
+                       quic_tls_discard_keys(qc->hel);
                        TRACE_PROTO("discarding Handshake pktns", QUIC_EV_CONN_PHPKTS, qc);
-                       quic_pktns_discard(qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE].pktns, qc);
+                       quic_pktns_discard(qc->hel->pktns, qc);
                        qc_set_timer(qc);
-                       qc_el_rx_pkts_del(&qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE]);
-                       qc_release_pktns_frms(qc, qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE].pktns);
+                       qc_el_rx_pkts_del(qc->hel);
+                       qc_release_pktns_frms(qc, qc->hel->pktns);
                }
 
-               if (qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE].pktns->flags & QUIC_FL_PKTNS_ACK_REQUIRED) {
+               if (qc->hel->pktns->flags & QUIC_FL_PKTNS_ACK_REQUIRED) {
                        /* There may be remaining handshake to build (acks) */
                        st = QUIC_HS_ST_SERVER_HANDSHAKE;
                }
@@ -5216,8 +5195,9 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state)
        BUG_ON_HOT(b_data(buf));
        b_reset(buf);
 
-       ret = qc_prep_pkts(qc, buf, tel, &qc->els[tel].pktns->tx.frms,
-                          next_tel, &qc->els[next_tel].pktns->tx.frms);
+       ret = qc_prep_hpkts(qc, buf,
+                           qc_quic_enc_level(qc, tel),
+                           qc_quic_enc_level(qc, next_tel));
        if (ret == -1) {
                qc_txb_release(qc);
                goto out;
@@ -5386,13 +5366,10 @@ struct task *qc_process_timer(struct task *task, void *ctx, unsigned int state)
                }
        }
        else if (!qc_is_listener(qc) && qc->state <= QUIC_HS_ST_COMPLETE) {
-               struct quic_enc_level *iel = &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL];
-               struct quic_enc_level *hel = &qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE];
-
-               if (quic_tls_has_tx_sec(hel))
-                       hel->pktns->tx.pto_probe = 1;
-               if (quic_tls_has_tx_sec(iel))
-                       iel->pktns->tx.pto_probe = 1;
+               if (quic_tls_has_tx_sec(qc->hel))
+                       qc->hel->pktns->tx.pto_probe = 1;
+               if (quic_tls_has_tx_sec(qc->iel))
+                       qc->iel->pktns->tx.pto_probe = 1;
        }
 
        tasklet_wakeup(qc->wait_event.tasklet);
@@ -5480,7 +5457,6 @@ static struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
        struct quic_conn *qc;
        struct listener *l = NULL;
        struct quic_cc_algo *cc_algo = NULL;
-       struct quic_tls_ctx *ictx, *app_ctx;
 
        TRACE_ENTER(QUIC_EV_CONN_INIT);
 
@@ -5526,30 +5502,15 @@ static struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
        quic_tls_ku_reset(&qc->ku.nxt_rx);
        quic_tls_ku_reset(&qc->ku.nxt_tx);
 
-       /* Encryption level: required to safely call quic_conn_enc_level_uninit()
-        * from quic_conn_release().
-        */
-       for (i = 0; i < QUIC_TLS_ENC_LEVEL_MAX; i++) {
-               struct quic_enc_level *qel = &qc->els[i];
-               struct quic_tls_ctx *ctx = &qel->tls_ctx;
-
-               quic_tls_ctx_reset(ctx);
-               qel->tx.crypto.nb_buf = 0;
-               qel->tx.crypto.bufs = NULL;
-               qel->cstream = NULL;
-               qel->pktns = NULL;
-       }
-
+       /* Encryption levels */
+       qc->iel = qc->eel = qc->hel = qc->ael = NULL;
+       LIST_INIT(&qc->qel_list);
        /* Packet number spaces */
        qc->ipktns = qc->hpktns = qc->apktns = NULL;
        LIST_INIT(&qc->pktns_list);
 
        quic_tls_ctx_reset(&qc->negotiated_ictx);
 
-       app_ctx = &qc->els[QUIC_TLS_ENC_LEVEL_APP].tls_ctx;
-       app_ctx->rx.secret = NULL;
-       app_ctx->tx.secret = NULL;
-
        /* Required to safely call quic_conn_prx_cntrs_update() from quic_conn_release(). */
        qc->prx_counters = NULL;
 
@@ -5607,12 +5568,7 @@ static struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
        /* Select our SCID which is the first CID with 0 as sequence number. */
        qc->scid = conn_id->cid;
 
-       /* Initial packet number spaces initialization. */
-       if (!quic_pktns_init(qc, &qc->ipktns))
-               goto err;
-
-       /* Initial encryption level initialization */
-       if (!quic_conn_enc_level_init(qc, qc->ipktns, QUIC_TLS_ENC_LEVEL_INITIAL)) {
+       if (!qc_enc_level_alloc(qc, &qc->ipktns, &qc->iel, ssl_encryption_initial)) {
                TRACE_ERROR("Could not initialize an encryption level", QUIC_EV_CONN_INIT, qc);
                goto err;
        }
@@ -5678,8 +5634,7 @@ static struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
            !quic_conn_init_idle_timer_task(qc))
                goto err;
 
-       ictx = &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL].tls_ctx;
-       if (!qc_new_isecs(qc, ictx,qc->original_version, dcid->data, dcid->len, 1))
+       if (!qc_new_isecs(qc, &qc->iel->tls_ctx, qc->original_version, dcid->data, dcid->len, 1))
                goto err;
 
        /* Counters initialization */
@@ -5728,10 +5683,8 @@ static inline void quic_conn_prx_cntrs_update(struct quic_conn *qc)
  */
 void quic_conn_release(struct quic_conn *qc)
 {
-       int i;
        struct ssl_sock_ctx *conn_ctx;
        struct eb64_node *node;
-       struct quic_tls_ctx *app_tls_ctx;
        struct quic_rx_packet *pkt, *pktback;
 
        TRACE_ENTER(QUIC_EV_CONN_CLOSE, qc);
@@ -5792,15 +5745,20 @@ void quic_conn_release(struct quic_conn *qc)
        }
 
        quic_tls_ku_free(qc);
-       for (i = 0; i < QUIC_TLS_ENC_LEVEL_MAX; i++) {
-               quic_tls_ctx_secs_free(&qc->els[i].tls_ctx);
-               quic_conn_enc_level_uninit(qc, &qc->els[i]);
+       if (qc->ael) {
+               struct quic_tls_ctx *actx = &qc->ael->tls_ctx;
+
+               /* Secrets used by keyupdate */
+               pool_free(pool_head_quic_tls_secret, actx->rx.secret);
+               pool_free(pool_head_quic_tls_secret, actx->tx.secret);
        }
-       quic_tls_ctx_secs_free(&qc->negotiated_ictx);
 
-       app_tls_ctx = &qc->els[QUIC_TLS_ENC_LEVEL_APP].tls_ctx;
-       pool_free(pool_head_quic_tls_secret, app_tls_ctx->rx.secret);
-       pool_free(pool_head_quic_tls_secret, app_tls_ctx->tx.secret);
+       qc_enc_level_free(qc, &qc->iel);
+       qc_enc_level_free(qc, &qc->eel);
+       qc_enc_level_free(qc, &qc->hel);
+       qc_enc_level_free(qc, &qc->ael);
+
+       quic_tls_ctx_secs_free(&qc->negotiated_ictx);
 
        quic_pktns_release(qc, &qc->ipktns);
        quic_pktns_release(qc, &qc->hpktns);
@@ -6092,7 +6050,7 @@ static inline int qc_try_rm_hp(struct quic_conn *qc,
        pn = beg + pkt->pn_offset;
 
        tel = quic_packet_type_enc_level(pkt->type);
-       qel = &qc->els[tel];
+       qel = qc_quic_enc_level(qc, tel);
 
        if (qc_qel_may_rm_hp(qc, qel)) {
                struct quic_tls_ctx *tls_ctx = qc_select_tls_ctx(qc, qel, pkt);
@@ -7802,8 +7760,7 @@ static void qc_build_cc_frm(struct quic_conn *qc, struct quic_enc_level *qel,
 
 
        if (qc->err.app) {
-               if (unlikely(qel == &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL] ||
-                            qel == &qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE])) {
+               if (unlikely(qel == qc->iel || qel == qc->hel)) {
                        /* RFC 9000 10.2.3.  Immediate Close during the Handshake
                         *
                         * Sending a CONNECTION_CLOSE of type 0x1d in an Initial or Handshake
index 99cd505896e93408f488fbe5dcc0b59d5eb38370..cb4c37fb249d9e1396dcbe51384a10029e6699a0 100644 (file)
@@ -106,20 +106,23 @@ void quic_tls_secret_hexdump(struct buffer *buf,
  * Returns 1 if succeeded, 0 if not. On error the caller is responsible to use
  * quic_conn_enc_level_uninit() to cleanup partially allocated content.
  */
-int quic_conn_enc_level_init(struct quic_conn *qc,
-                             enum quic_tls_enc_level level)
+static int quic_conn_enc_level_init(struct quic_conn *qc,
+                                    struct quic_enc_level **el,
+                                    struct quic_pktns *pktns,
+                                    enum ssl_encryption_level_t level)
 {
        int ret = 0;
        struct quic_enc_level *qel;
 
        TRACE_ENTER(QUIC_EV_CONN_CLOSE, qc);
 
-       qel = &qc->els[level];
-       qel->level = quic_to_ssl_enc_level(level);
-       qel->tls_ctx.rx.aead = qel->tls_ctx.tx.aead = NULL;
-       qel->tls_ctx.rx.md   = qel->tls_ctx.tx.md = NULL;
-       qel->tls_ctx.rx.hp   = qel->tls_ctx.tx.hp = NULL;
-       qel->tls_ctx.flags = 0;
+       qel = pool_alloc(pool_head_quic_enc_level);
+       if (!qel)
+               goto leave;
+
+       qel->pktns = pktns;
+       qel->level = level;
+       quic_tls_ctx_reset(&qel->tls_ctx);
 
        qel->rx.pkts = EB_ROOT;
        LIST_INIT(&qel->rx.pqpkts);
@@ -140,7 +143,7 @@ int quic_conn_enc_level_init(struct quic_conn *qc,
        qel->tx.crypto.sz = 0;
        qel->tx.crypto.offset = 0;
        /* No CRYPTO data for early data TLS encryption level */
-       if (level == QUIC_TLS_ENC_LEVEL_EARLY_DATA)
+       if (level == ssl_encryption_early_data)
                qel->cstream = NULL;
        else {
                qel->cstream = quic_cstream_new(qc);
@@ -148,6 +151,8 @@ int quic_conn_enc_level_init(struct quic_conn *qc,
                        goto leave;
        }
 
+       LIST_APPEND(&qc->qel_list, &qel->list);
+       *el = qel;
        ret = 1;
  leave:
        TRACE_LEAVE(QUIC_EV_CONN_CLOSE, qc);
@@ -173,6 +178,48 @@ void quic_conn_enc_level_uninit(struct quic_conn *qc, struct quic_enc_level *qel
        TRACE_LEAVE(QUIC_EV_CONN_CLOSE, qc);
 }
 
+/* Allocate a QUIC TLS encryption with <level> as TLS stack encryption to be
+ * attached to <qc> QUIC connection. Also allocate the associated packet number
+ * space object with <pktns> as address to be attached to <qc> if not already
+ * allocated.
+ * Return 1 if succeeded, 0 if not.
+ */
+int qc_enc_level_alloc(struct quic_conn *qc, struct quic_pktns **pktns,
+                       struct quic_enc_level **qel, enum ssl_encryption_level_t level)
+{
+       int ret = 0;
+
+       BUG_ON(!qel || !pktns);
+       BUG_ON(*qel && !*pktns);
+
+       if (!*pktns && !quic_pktns_init(qc, pktns))
+               goto leave;
+
+       if (!*qel && !quic_conn_enc_level_init(qc, qel, *pktns, level))
+           goto leave;
+
+       ret = 1;
+ leave:
+       return ret;
+}
+
+/* Free the memory allocated to the encryption level attached to <qc> connection
+ * with <qel> as pointer address. Also remove it from the list of the encryption
+ * levels attached to this connection and reset its value to NULL.
+ * Never fails.
+ */
+void qc_enc_level_free(struct quic_conn *qc, struct quic_enc_level **qel)
+{
+       if (!*qel)
+               return;
+
+       quic_tls_ctx_secs_free(&(*qel)->tls_ctx);
+       quic_conn_enc_level_uninit(qc, *qel);
+       LIST_DEL_INIT(&(*qel)->list);
+       pool_free(pool_head_quic_enc_level, *qel);
+       *qel = NULL;
+}
+
 int quic_hkdf_extract(const EVP_MD *md,
                       unsigned char *buf, size_t buflen,
                       const unsigned char *key, size_t keylen,