From: Frédéric Lécaille Date: Wed, 8 Jun 2022 17:28:36 +0000 (+0200) Subject: MEDIUM: quic: Add QUIC v2 draft support X-Git-Tag: v2.7-dev1~40 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=86845c517198ffb9a767a16e12c15e909dff2fc3;p=thirdparty%2Fhaproxy.git MEDIUM: quic: Add QUIC v2 draft support 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). --- diff --git a/include/haproxy/quic_tls-t.h b/include/haproxy/quic_tls-t.h index 646fa6294b..dd22235429 100644 --- a/include/haproxy/quic_tls-t.h +++ b/include/haproxy/quic_tls-t.h @@ -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, diff --git a/include/haproxy/quic_tls.h b/include/haproxy/quic_tls.h index 05b88999a9..244afe817f 100644 --- a/include/haproxy/quic_tls.h +++ b/include/haproxy/quic_tls.h @@ -30,18 +30,24 @@ /* 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, diff --git a/include/haproxy/xprt_quic-t.h b/include/haproxy/xprt_quic-t.h index 4f016df9f8..d472f62d5b 100644 --- a/include/haproxy/xprt_quic-t.h +++ b/include/haproxy/xprt_quic-t.h @@ -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 */ diff --git a/include/haproxy/xprt_quic.h b/include/haproxy/xprt_quic.h index d2fe63f28d..bbfeb04be1 100644 --- a/include/haproxy/xprt_quic.h +++ b/include/haproxy/xprt_quic.h @@ -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 version and */ +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; diff --git a/src/quic_tls.c b/src/quic_tls.c index bb25ff0d28..d39fd24fbc 100644 --- a/src/quic_tls.c +++ b/src/quic_tls.c @@ -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 secret key into 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) || diff --git a/src/xprt_quic.c b/src/xprt_quic.c index a899ec796c..9d59c459e1 100644 --- a/src/xprt_quic.c +++ b/src/xprt_quic.c @@ -57,15 +57,57 @@ #include /* 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. 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 first byte of 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 is supported. Returns a boolean. +/* Return the QUIC version (quic_version struct) with 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 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) {