]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: quic: Add QUIC v2 draft support
authorFrédéric Lécaille <flecaille@haproxy.com>
Wed, 8 Jun 2022 17:28:36 +0000 (19:28 +0200)
committerFrédéric Lécaille <flecaille@haproxy.com>
Thu, 16 Jun 2022 12:56:24 +0000 (14:56 +0200)
This is becoming difficult to handle the QUIC TLS related definitions
which arrive with a QUIC version (draft or not). So, here we add
quic_version C struct which does not define only the QUIC version number,
but also the QUIC TLS definitions which depend on a QUIC version.
Modify consequently all the QUIC TLS API to reuse these definitions
through new quic_version C struct.
Implement quic_pkt_type() function which return a packet type (0 up to 3)
depending on the QUIC version number.
Stop harding the Retry packet first byte in send_retry(): this is not more
possible because the packet type field depends on the QUIC version.
Also modify quic_build_packet_long_header() for the same reason: the packet
type depends on the QUIC version.
Add a quic_version C struct member to quic_conn C struct.
Modify qc_lstnr_pkt_rcv() to set this member asap.
Remove the version member from quic_rx_packet C struct: a packet is attached
asap to a connection (or dropped) which is the unique object which should
store the QUIC version.
Modify qc_pkt_is_supported_version() to return a supported quic_version C
struct from a version number.
Add Initial salt for QUIC v2 draft (initial_salt_v2_draft).

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

index 646fa6294b5818758a2e8d8d10c1c971e5f12064..dd2223542937a3bf3f2c46f6ef8d1e6aeb651c0b 100644 (file)
@@ -52,6 +52,29 @@ extern struct pool_head *pool_head_quic_tls_secret;
 extern struct pool_head *pool_head_quic_tls_iv;
 extern struct pool_head *pool_head_quic_tls_key;
 
+#define QUIC_HKDF_KEY_LABEL_V1 "quic key"
+#define QUIC_HKDF_IV_LABEL_V1  "quic iv"
+#define QUIC_HKDF_HP_LABEL_V1  "quic hp"
+#define QUIC_HKDF_KU_LABEL_V1  "quic ku"
+
+#define QUIC_HKDF_KEY_LABEL_V2 "quicv2 key"
+#define QUIC_HKDF_IV_LABEL_V2  "quicv2 iv"
+#define QUIC_HKDF_HP_LABEL_V2  "quicv2 hp"
+#define QUIC_HKDF_KU_LABEL_V2  "quicv2 ku"
+
+#define QUIC_TLS_RETRY_KEY_DRAFT \
+       "\xcc\xce\x18\x7e\xd0\x9a\x09\xd0\x57\x28\x15\x5a\x6c\xb9\x6b\xe1"
+#define QUIC_TLS_RETRY_NONCE_DRAFT \
+       "\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c"
+#define QUIC_TLS_RETRY_KEY_V1 \
+       "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e"
+#define QUIC_TLS_RETRY_NONCE_V1 \
+       "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb"
+#define QUIC_TLS_RETRY_KEY_V2_DRAFT \
+       "\xba\x85\x8d\xc7\xb4\x3d\xe5\xdb\xf8\x76\x17\xff\x4a\xb2\x53\xdb"
+#define QUIC_TLS_RETRY_NONCE_V2_DRAFT \
+       "\x14\x1b\x99\xc2\x39\xb0\x3e\x78\x5d\x6a\x2e\x9f"
+
 /* QUIC handshake states for both clients and servers. */
 enum quic_handshake_state {
        QUIC_HS_ST_CLIENT_HANDSHAKE_FAILED,
index 05b88999a9463ab791217d1b87d9783bd485e488..244afe817f52e5f54006666c4636ee414a6cf4eb 100644 (file)
 /* Initial salt depending on QUIC version to derive client/server initial secrets.
  * This one is for draft-29 QUIC version.
  */
-unsigned char initial_salt_draft_29[20] = {
+const unsigned char initial_salt_draft_29[20] = {
        0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c,
        0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0,
        0x43, 0x90, 0xa8, 0x99
 };
 
-unsigned char initial_salt_v1[20] = {
+const unsigned char initial_salt_v1[20] = {
        0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3,
        0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad,
        0xcc, 0xbb, 0x7f, 0x0a
 };
 
+const unsigned char initial_salt_v2_draft[20] = {
+       0xa7, 0x07, 0xc2, 0x03, 0xa5, 0x9b, 0x47, 0x18,
+       0x4a, 0x1d, 0x62, 0xca, 0x57, 0x04, 0x06, 0xea,
+       0x7a, 0xe3, 0xe5, 0xd3
+};
+
 void quic_tls_keys_hexdump(struct buffer *buf,
                            const struct quic_tls_secrets *secs);
 
@@ -76,10 +82,11 @@ int quic_tls_decrypt(unsigned char *buf, size_t len,
                      const unsigned char *key, const unsigned char *iv);
 
 int quic_tls_generate_retry_integrity_tag(unsigned char *odcid, size_t odcid_len,
-                                          unsigned char *buf, size_t len, uint32_t version);
+                                          unsigned char *buf, size_t len,
+                                          const struct quic_version *qv);
 
 int quic_tls_derive_keys(const EVP_CIPHER *aead, const EVP_CIPHER *hp,
-                         const EVP_MD *md,
+                         const EVP_MD *md, const struct quic_version *qv,
                          unsigned char *key, size_t keylen,
                          unsigned char *iv, size_t ivlen,
                          unsigned char *hp_key, size_t hp_keylen,
@@ -102,7 +109,7 @@ int quic_tls_rx_ctx_init(EVP_CIPHER_CTX **rx_ctx,
 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,
+int quic_tls_sec_update(const EVP_MD *md, const struct quic_version *qv,
                         unsigned char *new_sec, size_t new_seclen,
                         const unsigned char *sec, size_t seclen);
 
@@ -498,7 +505,6 @@ static inline void quic_tls_discard_keys(struct quic_enc_level *qel)
  * Return 1 if succeeded or 0 if not.
  */
 static inline int qc_new_isecs(struct quic_conn *qc,
-                               const unsigned char *salt, size_t salt_len,
                                const unsigned char *cid, size_t cidlen, int server)
 {
        unsigned char initial_secret[32];
@@ -515,7 +521,7 @@ static inline int qc_new_isecs(struct quic_conn *qc,
                goto err;
 
        if (!quic_derive_initial_secret(ctx->rx.md,
-                                       salt, salt_len,
+                                       qc->version->initial_salt, qc->version->initial_salt_len,
                                        initial_secret, sizeof initial_secret,
                                        cid, cidlen))
                goto err;
@@ -528,7 +534,7 @@ static inline int qc_new_isecs(struct quic_conn *qc,
 
        rx_ctx = &ctx->rx;
        tx_ctx = &ctx->tx;
-       if (!quic_tls_derive_keys(ctx->rx.aead, ctx->rx.hp, ctx->rx.md,
+       if (!quic_tls_derive_keys(ctx->rx.aead, ctx->rx.hp, ctx->rx.md, qc->version,
                                  rx_ctx->key, rx_ctx->keylen,
                                  rx_ctx->iv, rx_ctx->ivlen,
                                  rx_ctx->hp_key, sizeof rx_ctx->hp_key,
@@ -538,7 +544,7 @@ static inline int qc_new_isecs(struct quic_conn *qc,
        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,
+       if (!quic_tls_derive_keys(ctx->tx.aead, ctx->tx.hp, ctx->tx.md, qc->version,
                                  tx_ctx->key, tx_ctx->keylen,
                                  tx_ctx->iv, tx_ctx->ivlen,
                                  tx_ctx->hp_key, sizeof tx_ctx->hp_key,
index 4f016df9f88377ddcc334e3dafc7242e6177a351..d472f62d5bbb8e49c44081c24e9071c032fb43e2 100644 (file)
@@ -269,6 +269,23 @@ extern struct pool_head *pool_head_quic_tx_packet;
 extern struct pool_head *pool_head_quic_frame;
 extern struct pool_head *pool_head_quic_dgram;
 
+struct quic_version {
+       uint32_t num;
+       const unsigned char *initial_salt;
+       size_t initial_salt_len;
+       const unsigned char *key_label;
+       size_t key_label_len;
+       const unsigned char *iv_label;
+       size_t iv_label_len;
+       const unsigned char *hp_label;
+       size_t hp_label_len;
+       const unsigned char *ku_label;
+       size_t ku_label_len;
+       /* Retry tag */
+       const unsigned char *retry_tag_key;
+       const unsigned char *retry_tag_nonce;
+};
+
 /* QUIC connection id data.
  *
  * This struct is used by ebmb_node structs as last member of flexible arrays.
@@ -397,7 +414,6 @@ struct quic_rx_packet {
        struct list qc_rx_pkt_list;
        struct quic_conn *qc;
        unsigned char type;
-       uint32_t version;
        /* Initial desctination connection ID. */
        struct quic_cid dcid;
        struct quic_cid scid;
@@ -609,7 +625,7 @@ enum qc_mux_state {
 #define QUIC_FL_CONN_DRAINING                    (1U << 30)
 #define QUIC_FL_CONN_IMMEDIATE_CLOSE             (1U << 31)
 struct quic_conn {
-       uint32_t version;
+       const struct quic_version *version;
        /* QUIC transport parameters TLS extension */
        int tps_tls_ext;
        /* Thread ID this connection is attached to */
index d2fe63f28db118126d4511403f5671e3f45355d8..bbfeb04be1944d49f1eb19e868f1acd91519bf08 100644 (file)
@@ -52,6 +52,26 @@ extern struct pool_head *pool_head_quic_connection_id;
 
 int ssl_quic_initial_ctx(struct bind_conf *bind_conf);
 
+/* Return the long packet type matching with <qv> version and <type> */
+static inline int quic_pkt_type(int type, uint32_t version)
+{
+       if (version != QUIC_PROTOCOL_VERSION_2_DRAFT)
+               return type;
+
+       switch (type) {
+       case QUIC_PACKET_TYPE_INITIAL:
+               return 1;
+       case QUIC_PACKET_TYPE_0RTT:
+               return 2;
+       case QUIC_PACKET_TYPE_HANDSHAKE:
+               return 3;
+       case QUIC_PACKET_TYPE_RETRY:
+               return 0;
+       }
+
+       return -1;
+}
+
 static inline int qc_is_listener(struct quic_conn *qc)
 {
        return qc->flags & QUIC_FL_CONN_LISTENER;
index bb25ff0d285b3584b12fb8d214dd3d4b55132a9c..d39fd24fbc130e7f9c067a9d445be5a80eeff095 100644 (file)
@@ -19,37 +19,6 @@ DECLARE_POOL(pool_head_quic_tls_secret, "quic_tls_secret", QUIC_TLS_SECRET_LEN);
 DECLARE_POOL(pool_head_quic_tls_iv,     "quic_tls_iv",     QUIC_TLS_IV_LEN);
 DECLARE_POOL(pool_head_quic_tls_key,    "quic_tls_key",    QUIC_TLS_KEY_LEN);
 
-/* key/nonce from rfc9001 5.8. Retry Packet Integrity */
-const unsigned char key_v1[] = {
-       0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a,
-       0x1d, 0x76, 0x6b, 0x54, 0xe3, 0x68, 0xc8, 0x4e,
-};
-
-const unsigned char nonce_v1[] = {
-       0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2,
-       0x23, 0x98, 0x25, 0xbb,
-};
-
-const unsigned char key_v2[] = {
-       0xba, 0x85, 0x8d, 0xc7, 0xb4, 0x3d, 0xe5, 0xdb,
-       0xf8, 0x76, 0x17, 0xff, 0x4a, 0xb2, 0x53, 0xdb,
-};
-
-const unsigned char nonce_v2[] = {
-       0x14, 0x1b, 0x99, 0xc2, 0x39, 0xb0, 0x3e, 0x78,
-       0x5d, 0x6a, 0x2e, 0x9f,
-};
-
-const unsigned char key_draft[] = {
-       0xcc, 0xce, 0x18, 0x7e, 0xd0, 0x9a, 0x09, 0xd0,
-       0x57, 0x28, 0x15, 0x5a, 0x6c, 0xb9, 0x6b, 0xe1,
-};
-
-const unsigned char nonce_draft[] = {
-       0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, 0x36, 0xf0,
-       0x53, 0x0a, 0x8c, 0x1c,
-};
-
 __attribute__((format (printf, 3, 4)))
 void hexdump(const void *buf, size_t buflen, const char *title_fmt, ...);
 
@@ -251,7 +220,7 @@ int quic_hkdf_expand_label(const EVP_MD *md,
  * Obviouly these keys have the same size becaused derived with the same TLS cryptographic context.
  */
 int quic_tls_derive_keys(const EVP_CIPHER *aead, const EVP_CIPHER *hp,
-                         const EVP_MD *md,
+                         const EVP_MD *md, const struct quic_version *qv,
                          unsigned char *key, size_t keylen,
                          unsigned char *iv, size_t ivlen,
                          unsigned char *hp_key, size_t hp_keylen,
@@ -260,19 +229,16 @@ int quic_tls_derive_keys(const EVP_CIPHER *aead, const EVP_CIPHER *hp,
        size_t aead_keylen = (size_t)EVP_CIPHER_key_length(aead);
        size_t aead_ivlen = (size_t)EVP_CIPHER_iv_length(aead);
        size_t hp_len = hp ? (size_t)EVP_CIPHER_key_length(hp) : 0;
-       const unsigned char    key_label[] = "quic key";
-       const unsigned char     iv_label[] = "quic iv";
-       const unsigned char hp_key_label[] = "quic hp";
 
        if (aead_keylen > keylen || aead_ivlen > ivlen || hp_len > hp_keylen)
                return 0;
 
        if (!quic_hkdf_expand_label(md, key, aead_keylen, secret, secretlen,
-                                   key_label, sizeof key_label - 1) ||
+                                   qv->key_label,qv->key_label_len) ||
            !quic_hkdf_expand_label(md, iv, aead_ivlen, secret, secretlen,
-                                   iv_label, sizeof iv_label - 1) ||
+                                   qv->iv_label, qv->iv_label_len) ||
            (hp_key && !quic_hkdf_expand_label(md, hp_key, hp_len, secret, secretlen,
-                                              hp_key_label, sizeof hp_key_label - 1)))
+                                              qv->hp_label, qv->hp_label_len)))
                return 0;
 
        return 1;
@@ -334,14 +300,12 @@ int quic_tls_derive_initial_secrets(const EVP_MD *md,
 /* Update <sec> secret key into <new_sec> according to RFC 9001 6.1.
  * Always succeeds.
  */
-int quic_tls_sec_update(const EVP_MD *md,
+int quic_tls_sec_update(const EVP_MD *md, const struct quic_version *qv,
                         unsigned char *new_sec, size_t new_seclen,
                         const unsigned char *sec, size_t seclen)
 {
-       const unsigned char ku_label[] = "quic ku";
-
        return quic_hkdf_expand_label(md, new_sec, new_seclen, sec, seclen,
-                                     ku_label, sizeof ku_label - 1);
+                                     qv->ku_label, qv->ku_label_len);
 }
 
 /*
@@ -593,11 +557,10 @@ int quic_tls_derive_retry_token_secret(const EVP_MD *md,
  */
 int quic_tls_generate_retry_integrity_tag(unsigned char *odcid, unsigned char odcid_len,
                                           unsigned char *pkt, size_t pkt_len,
-                                          uint32_t version)
+                                          const struct quic_version *qv)
 {
        const EVP_CIPHER *evp = EVP_aes_128_gcm();
        EVP_CIPHER_CTX *ctx;
-       const unsigned char *key, *nonce;
 
        /* encryption buffer - not used as only AEAD tag generation is proceed */
        unsigned char *out = NULL;
@@ -609,26 +572,12 @@ int quic_tls_generate_retry_integrity_tag(unsigned char *odcid, unsigned char od
        if (!ctx)
                return 0;
 
-       switch (version) {
-       case QUIC_PROTOCOL_VERSION_1:
-               key = key_v1;
-               nonce = nonce_v1;
-               break;
-       case QUIC_PROTOCOL_VERSION_2_DRAFT:
-               key = key_v2;
-               nonce = nonce_v2;
-               break;
-       default:
-               key = key_draft;
-               nonce = nonce_draft;
-       }
-
        /* rfc9001 5.8. Retry Packet Integrity
         *
         * AEAD is proceed over a pseudo-Retry packet used as AAD. It contains
         * the ODCID len + data and the Retry packet itself.
         */
-       if (!EVP_EncryptInit_ex(ctx, evp, NULL, key, nonce) ||
+       if (!EVP_EncryptInit_ex(ctx, evp, NULL, qv->retry_tag_key, qv->retry_tag_nonce) ||
            /* specify pseudo-Retry as AAD */
            !EVP_EncryptUpdate(ctx, NULL, &outlen, &odcid_len, sizeof(odcid_len)) ||
            !EVP_EncryptUpdate(ctx, NULL, &outlen, odcid, odcid_len) ||
index a899ec796c0376cab1cf932f8c961e7c3bf267d9..9d59c459e1bf4fde723f9156b0ec09360ecc49a6 100644 (file)
 #include <haproxy/xprt_quic.h>
 
 /* list of supported QUIC versions by this implementation */
-static int quic_supported_version[] = {
-       0x00000001,
-       0x709a50c4, /* V2 draft */
-       0xff00001d, /* draft-29 */
-
-       /* placeholder, do not add entry after this */
-       0x0
+static const struct quic_version quic_versions[] = {
+       {
+               .num              = QUIC_PROTOCOL_VERSION_DRAFT_29,
+               .initial_salt     = initial_salt_draft_29,
+               .initial_salt_len = sizeof initial_salt_draft_29,
+               .key_label        = (const unsigned char *)QUIC_HKDF_KEY_LABEL_V1,
+               .key_label_len    = strlen(QUIC_HKDF_KEY_LABEL_V1),
+               .iv_label         = (const unsigned char *)QUIC_HKDF_IV_LABEL_V1,
+               .iv_label_len     = strlen(QUIC_HKDF_IV_LABEL_V1),
+               .hp_label         = (const unsigned char *)QUIC_HKDF_HP_LABEL_V1,
+               .hp_label_len     = strlen(QUIC_HKDF_HP_LABEL_V1),
+               .ku_label         = (const unsigned char *)QUIC_HKDF_KU_LABEL_V1,
+               .ku_label_len     = strlen(QUIC_HKDF_KU_LABEL_V1),
+               .retry_tag_key    = (const unsigned char *)QUIC_TLS_RETRY_KEY_DRAFT,
+               .retry_tag_nonce  = (const unsigned char *)QUIC_TLS_RETRY_NONCE_DRAFT,
+       },
+       {
+               .num              = QUIC_PROTOCOL_VERSION_1,
+               .initial_salt     = initial_salt_v1,
+               .initial_salt_len = sizeof initial_salt_v1,
+               .key_label        = (const unsigned char *)QUIC_HKDF_KEY_LABEL_V1,
+               .key_label_len    = strlen(QUIC_HKDF_KEY_LABEL_V1),
+               .iv_label         = (const unsigned char *)QUIC_HKDF_IV_LABEL_V1,
+               .iv_label_len     = strlen(QUIC_HKDF_IV_LABEL_V1),
+               .hp_label         = (const unsigned char *)QUIC_HKDF_HP_LABEL_V1,
+               .hp_label_len     = strlen(QUIC_HKDF_HP_LABEL_V1),
+               .ku_label         = (const unsigned char *)QUIC_HKDF_KU_LABEL_V1,
+               .ku_label_len     = strlen(QUIC_HKDF_KU_LABEL_V1),
+               .retry_tag_key    = (const unsigned char *)QUIC_TLS_RETRY_KEY_V1,
+               .retry_tag_nonce  = (const unsigned char *)QUIC_TLS_RETRY_NONCE_V1,
+       },
+       {
+               .num              = QUIC_PROTOCOL_VERSION_2_DRAFT,
+               .initial_salt     = initial_salt_v2_draft,
+               .initial_salt_len = sizeof initial_salt_v2_draft,
+               .key_label        = (const unsigned char *)QUIC_HKDF_KEY_LABEL_V2,
+               .key_label_len    = strlen(QUIC_HKDF_KEY_LABEL_V2),
+               .iv_label         = (const unsigned char *)QUIC_HKDF_IV_LABEL_V2,
+               .iv_label_len     = strlen(QUIC_HKDF_IV_LABEL_V2),
+               .hp_label         = (const unsigned char *)QUIC_HKDF_HP_LABEL_V2,
+               .hp_label_len     = strlen(QUIC_HKDF_HP_LABEL_V2),
+               .ku_label         = (const unsigned char *)QUIC_HKDF_KU_LABEL_V2,
+               .ku_label_len     = strlen(QUIC_HKDF_KU_LABEL_V2),
+               .retry_tag_key    = (const unsigned char *)QUIC_TLS_RETRY_KEY_V2_DRAFT,
+               .retry_tag_nonce  = (const unsigned char *)QUIC_TLS_RETRY_NONCE_V2_DRAFT,
+       },
 };
 
+/* The total number of supported versions */
+static size_t quic_versions_nb = sizeof quic_versions / sizeof *quic_versions;
+
 /* trace source and events */
 static void quic_trace(enum trace_level level, uint64_t mask, \
                        const struct trace_source *src,
@@ -710,13 +752,13 @@ static int quic_tls_key_update(struct quic_conn *qc)
        nxt_tx = &qc->ku.nxt_tx;
 
        /* Prepare new RX secrets */
-       if (!quic_tls_sec_update(rx->md, nxt_rx->secret, nxt_rx->secretlen,
+       if (!quic_tls_sec_update(rx->md, qc->version, nxt_rx->secret, nxt_rx->secretlen,
                                 rx->secret, rx->secretlen)) {
                TRACE_DEVEL("New RX secret update failed", QUIC_EV_CONN_RWSEC, qc);
                return 0;
        }
 
-       if (!quic_tls_derive_keys(rx->aead, NULL, rx->md,
+       if (!quic_tls_derive_keys(rx->aead, NULL, rx->md, qc->version,
                                  nxt_rx->key, nxt_rx->keylen,
                                  nxt_rx->iv, nxt_rx->ivlen, NULL, 0,
                                  nxt_rx->secret, nxt_rx->secretlen)) {
@@ -725,13 +767,13 @@ static int quic_tls_key_update(struct quic_conn *qc)
        }
 
        /* Prepare new TX secrets */
-       if (!quic_tls_sec_update(tx->md, nxt_tx->secret, nxt_tx->secretlen,
+       if (!quic_tls_sec_update(tx->md, qc->version, nxt_tx->secret, nxt_tx->secretlen,
                                 tx->secret, tx->secretlen)) {
                TRACE_DEVEL("New TX secret update failed", QUIC_EV_CONN_RWSEC, qc);
                return 0;
        }
 
-       if (!quic_tls_derive_keys(tx->aead, NULL, tx->md,
+       if (!quic_tls_derive_keys(tx->aead, NULL, tx->md, qc->version,
                                  nxt_tx->key, nxt_tx->keylen,
                                  nxt_tx->iv, nxt_tx->ivlen, NULL, 0,
                                  nxt_tx->secret, nxt_tx->secretlen)) {
@@ -840,7 +882,7 @@ int ha_quic_set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t level,
        rx->md   = tx->md   = tls_md(cipher);
        rx->hp   = tx->hp   = tls_hp(cipher);
 
-       if (!quic_tls_derive_keys(rx->aead, rx->hp, rx->md, rx->key, rx->keylen,
+       if (!quic_tls_derive_keys(rx->aead, rx->hp, rx->md, qc->version, rx->key, rx->keylen,
                                  rx->iv, rx->ivlen, rx->hp_key, sizeof rx->hp_key,
                                  read_secret, secret_len)) {
                TRACE_DEVEL("RX key derivation failed", QUIC_EV_CONN_RWSEC, qc);
@@ -862,7 +904,7 @@ int ha_quic_set_encryption_secrets(SSL *ssl, enum ssl_encryption_level_t level,
        if (!write_secret)
                goto out;
 
-       if (!quic_tls_derive_keys(tx->aead, tx->hp, tx->md, tx->key, tx->keylen,
+       if (!quic_tls_derive_keys(tx->aead, tx->hp, tx->md, qc->version, tx->key, tx->keylen,
                                  tx->iv, tx->ivlen, tx->hp_key, sizeof tx->hp_key,
                                  write_secret, secret_len)) {
                TRACE_DEVEL("TX key derivation failed", QUIC_EV_CONN_RWSEC, qc);
@@ -4274,7 +4316,7 @@ static int parse_retry_token(const unsigned char *token, const unsigned char *en
  * length. <saddr> is the source address.
  * Returns the connection if succeeded, NULL if not.
  */
-static struct quic_conn *qc_new_conn(unsigned int version, int ipv4,
+static struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
                                      struct quic_cid *dcid, struct quic_cid *scid,
                                      const struct quic_cid *odcid,
                                      struct sockaddr_storage *saddr,
@@ -4286,8 +4328,6 @@ static struct quic_conn *qc_new_conn(unsigned int version, int ipv4,
        struct quic_connection_id *icid;
        char *buf_area = NULL;
        struct listener *l = NULL;
-       const unsigned char *salt = initial_salt_v1;
-       size_t salt_len = sizeof initial_salt_v1;
 
        TRACE_ENTER(QUIC_EV_CONN_INIT);
        qc = pool_zalloc(pool_head_quic_conn);
@@ -4361,8 +4401,8 @@ static struct quic_conn *qc_new_conn(unsigned int version, int ipv4,
                qc->els[i].pktns = &qc->pktns[quic_tls_pktns(i)];
        }
 
-       qc->version = version;
-       qc->tps_tls_ext = qc->version & 0xff000000 ?
+       qc->version = qv;
+       qc->tps_tls_ext = qc->version->num & 0xff000000 ?
                TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS_DRAFT:
                TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS;
        /* TX part. */
@@ -4416,12 +4456,7 @@ static struct quic_conn *qc_new_conn(unsigned int version, int ipv4,
            !quic_conn_init_idle_timer_task(qc))
                goto err;
 
-       if (version == QUIC_PROTOCOL_VERSION_DRAFT_29) {
-               salt = initial_salt_draft_29;
-               salt_len = sizeof initial_salt_draft_29;
-       }
-
-       if (!qc_new_isecs(qc, salt, salt_len, dcid->data, dcid->len, 1))
+       if (!qc_new_isecs(qc, dcid->data, dcid->len, 1))
                goto err;
 
        TRACE_LEAVE(QUIC_EV_CONN_INIT, qc);
@@ -4662,11 +4697,12 @@ static inline int qc_try_rm_hp(struct quic_conn *qc,
 }
 
 /* Parse the header form from <byte0> first byte of <pkt> packet to set its type.
- * Also set <*long_header> to 1 if this form is long, 0 if not.
+ * Also set <*long_header> to 1 if this form is long, 0 if not and the version
+ * of this packet into <*version>.
  */
 static inline int qc_parse_hd_form(struct quic_rx_packet *pkt,
                                    unsigned char **buf, const unsigned char *end,
-                                   int *long_header)
+                                   int *long_header, uint32_t *version)
 {
        const unsigned char byte0 = **buf;
 
@@ -4677,10 +4713,10 @@ static inline int qc_parse_hd_form(struct quic_rx_packet *pkt,
 
                *long_header = 1;
                /* Version */
-               if (!quic_read_uint32(&pkt->version, (const unsigned char **)buf, end))
+               if (!quic_read_uint32(version, (const unsigned char **)buf, end))
                        return 0;
 
-               if (pkt->version != QUIC_PROTOCOL_VERSION_2_DRAFT) {
+               if (*version != QUIC_PROTOCOL_VERSION_2_DRAFT) {
                        pkt->type = type;
                }
                else {
@@ -4708,22 +4744,18 @@ static inline int qc_parse_hd_form(struct quic_rx_packet *pkt,
        return 1;
 }
 
-/*
- * Check if the QUIC version in packet <pkt> is supported. Returns a boolean.
+/* Return the QUIC version (quic_version struct) with <version> as version number
+ * if supported or NULL if not.
  */
-static inline int qc_pkt_is_supported_version(struct quic_rx_packet *pkt)
+static inline const struct quic_version *qc_supported_version(uint32_t version)
 {
-       int j = 0, version;
-
-       do {
-               version = quic_supported_version[j];
-               if (version == pkt->version)
-                       return 1;
+       int i;
 
-               version = quic_supported_version[++j];
-       } while(version);
+       for (i = 0; i < quic_versions_nb; i++)
+               if (quic_versions[i].num == version)
+                       return &quic_versions[i];
 
-       return 0;
+       return NULL;
 }
 
 /*
@@ -4739,7 +4771,8 @@ static int send_version_negotiation(int fd, struct sockaddr_storage *addr,
                                     struct quic_rx_packet *pkt)
 {
        char buf[256];
-       int i = 0, j, version;
+       int i = 0, j;
+       uint32_t version;
        const socklen_t addrlen = get_addr_len(addr);
 
        /*
@@ -4767,14 +4800,12 @@ static int send_version_negotiation(int fd, struct sockaddr_storage *addr,
        i += pkt->dcid.len;
 
        /* supported version */
-       j = 0;
-       do {
-               version = htonl(quic_supported_version[j]);
+       for (j = 0; j < quic_versions_nb; j++) {
+               version = htonl(quic_versions[j].num);
                memcpy(&buf[i], &version, sizeof(version));
                i += sizeof(version);
+       }
 
-               version = quic_supported_version[++j];
-       } while (version);
 
        if (sendto(fd, buf, i, 0, (struct sockaddr *)addr, addrlen) < 0)
                return 1;
@@ -4992,20 +5023,21 @@ static int quic_retry_token_check(unsigned char *token, size_t tokenlen,
  * Returns 0 on success else non-zero.
  */
 static int send_retry(int fd, struct sockaddr_storage *addr,
-                      struct quic_rx_packet *pkt)
+                      struct quic_rx_packet *pkt, const struct quic_version *qv)
 {
        unsigned char buf[128];
        int i = 0, token_len;
        const socklen_t addrlen = get_addr_len(addr);
        struct quic_cid scid;
 
-       /* long header + fixed bit + packet type 0x3 */
-       buf[i++] = 0xf0;
+       /* long header + fixed bit + packet type QUIC_PACKET_TYPE_RETRY */
+       buf[i++] = (QUIC_PACKET_LONG_HEADER_BIT | QUIC_PACKET_FIXED_BIT) |
+               (quic_pkt_type(QUIC_PACKET_TYPE_RETRY, qv->num) << QUIC_PACKET_TYPE_SHIFT);
        /* version */
-       buf[i++] = *((unsigned char *)&pkt->version + 3);
-       buf[i++] = *((unsigned char *)&pkt->version + 2);
-       buf[i++] = *((unsigned char *)&pkt->version + 1);
-       buf[i++] = *(unsigned char *)&pkt->version;
+       buf[i++] = *((unsigned char *)&qv->num + 3);
+       buf[i++] = *((unsigned char *)&qv->num + 2);
+       buf[i++] = *((unsigned char *)&qv->num + 1);
+       buf[i++] = *(unsigned char *)&qv->num;
 
        /* Use the SCID from <pkt> for Retry DCID. */
        buf[i++] = pkt->scid.len;
@@ -5022,7 +5054,7 @@ static int send_retry(int fd, struct sockaddr_storage *addr,
        i += scid.len;
 
        /* token */
-       if (!(token_len = quic_generate_retry_token(&buf[i], sizeof(buf) - i, pkt->version,
+       if (!(token_len = quic_generate_retry_token(&buf[i], sizeof(buf) - i, qv->num,
                                                    &pkt->dcid, &pkt->scid, addr)))
                return 1;
 
@@ -5031,7 +5063,7 @@ static int send_retry(int fd, struct sockaddr_storage *addr,
        /* token integrity tag */
        if ((&buf[i] - buf < QUIC_TLS_TAG_LEN) ||
            !quic_tls_generate_retry_integrity_tag(pkt->dcid.data,
-                                                  pkt->dcid.len, buf, i, pkt->version)) {
+                                                  pkt->dcid.len, buf, i, qv)) {
                return 1;
        }
 
@@ -5236,6 +5268,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end,
        int drop_no_conn = 0, long_header = 0, io_cb_wakeup = 0;
        size_t b_cspace;
        struct quic_enc_level *qel;
+       uint32_t version;
 
        beg = buf;
        qc = NULL;
@@ -5271,7 +5304,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end,
        }
 
        /* Header form */
-       if (!qc_parse_hd_form(pkt, &buf, end, &long_header)) {
+       if (!qc_parse_hd_form(pkt, &buf, end, &long_header, &version)) {
                TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
                goto drop;
        }
@@ -5279,6 +5312,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end,
        if (long_header) {
                uint64_t len;
                struct quic_cid odcid;
+               const struct quic_version *qv;
 
                if (!quic_packet_read_long_header(&buf, end, pkt)) {
                        TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
@@ -5306,13 +5340,14 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end,
                }
 
                /* Retry of Version Negotiation packets are only sent by servers */
-               if (pkt->type == QUIC_PACKET_TYPE_RETRY || !pkt->version) {
+               if (pkt->type == QUIC_PACKET_TYPE_RETRY || !version) {
                        TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT);
                        goto drop;
                }
 
                /* RFC9000 6. Version Negotiation */
-               if (!qc_pkt_is_supported_version(pkt)) {
+               qv = qc_supported_version(version);
+               if (!qv) {
                         /* unsupported version, send Negotiation packet */
                        if (send_version_negotiation(l->rx.fd, &dgram->saddr, pkt)) {
                                TRACE_PROTO("Error on Version Negotiation sending", QUIC_EV_CONN_LPKT);
@@ -5342,7 +5377,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end,
                                if (!token_len) {
                                        if (l->bind_conf->options & BC_O_QUIC_FORCE_RETRY) {
                                                TRACE_PROTO("Initial without token, sending retry", QUIC_EV_CONN_LPKT);
-                                               if (send_retry(l->rx.fd, &dgram->saddr, pkt)) {
+                                               if (send_retry(l->rx.fd, &dgram->saddr, pkt, qv)) {
                                                        TRACE_PROTO("Error during Retry generation", QUIC_EV_CONN_LPKT);
                                                        goto err;
                                                }
@@ -5353,7 +5388,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end,
                                }
                                else {
                                        if (*buf == QUIC_TOKEN_FMT_RETRY) {
-                                               if (!quic_retry_token_check(buf, token_len, pkt->version, &odcid,
+                                               if (!quic_retry_token_check(buf, token_len, version, &odcid,
                                                                            &pkt->scid, qc, &dgram->saddr)) {
                                                        HA_ATOMIC_INC(&prx_counters->retry_error);
                                                        TRACE_PROTO("Wrong retry token", QUIC_EV_CONN_LPKT);
@@ -5408,7 +5443,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end,
                        if (global.cluster_secret && !pkt->token_len && !(l->bind_conf->options & BC_O_QUIC_FORCE_RETRY) &&
                            HA_ATOMIC_LOAD(&prx_counters->half_open_conn) >= global.tune.quic_retry_threshold) {
                                TRACE_PROTO("Initial without token, sending retry", QUIC_EV_CONN_LPKT);
-                               if (send_retry(l->rx.fd, &dgram->saddr, pkt)) {
+                               if (send_retry(l->rx.fd, &dgram->saddr, pkt, qv)) {
                                        TRACE_PROTO("Error during Retry generation", QUIC_EV_CONN_LPKT);
                                        goto err;
                                }
@@ -5430,7 +5465,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end,
 
                        pkt->saddr = dgram->saddr;
                        ipv4 = dgram->saddr.ss_family == AF_INET;
-                       qc = qc_new_conn(pkt->version, ipv4, &pkt->dcid, &pkt->scid, &odcid,
+                       qc = qc_new_conn(qv, ipv4, &pkt->dcid, &pkt->scid, &odcid,
                                         &pkt->saddr, 1, !!pkt->token_len, l);
                        if (qc == NULL)
                                goto drop;
@@ -5613,11 +5648,12 @@ static int quic_build_packet_long_header(unsigned char **buf, const unsigned cha
        if (end - *buf < sizeof conn->version + conn->dcid.len + conn->scid.len + 3)
                return 0;
 
+       type = quic_pkt_type(type, conn->version->num);
        /* #0 byte flags */
        *(*buf)++ = QUIC_PACKET_FIXED_BIT | QUIC_PACKET_LONG_HEADER_BIT |
                (type << QUIC_PACKET_TYPE_SHIFT) | (pn_len - 1);
        /* Version */
-       quic_write_uint32(buf, end, conn->version);
+       quic_write_uint32(buf, end, conn->version->num);
        *(*buf)++ = conn->dcid.len;
        /* Destination connection ID */
        if (conn->dcid.len) {