From: Frédéric Lécaille Date: Tue, 28 Nov 2023 10:25:04 +0000 (+0100) Subject: REORG: quic: Add a new module for QUIC retry X-Git-Tag: v2.9-dev12~53 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b5970967ca135c43b071ef58c1dce2b614a1a413;p=thirdparty%2Fhaproxy.git REORG: quic: Add a new module for QUIC retry Add quic_retry.c new C file for the QUIC retry feature: quic_saddr_cpy() moved from quic_tx.c, quic_generate_retry_token_aad() moved from quic_generate_retry_token() moved from parse_retry_token() moved from quic_retry_token_check() moved from quic_retry_token_check() moved from --- diff --git a/Makefile b/Makefile index 48734b98f9..c4464122ec 100644 --- a/Makefile +++ b/Makefile @@ -620,7 +620,7 @@ OPTIONS_OBJS += src/quic_conn.o src/mux_quic.o src/h3.o src/xprt_quic.o \ src/h3_stats.o src/qmux_http.o src/cfgparse-quic.o \ src/cbuf.o src/quic_cc.o src/quic_cc_nocc.o src/quic_ack.o \ src/quic_trace.o src/quic_cli.o src/quic_ssl.o \ - src/quic_rx.o src/quic_tx.o src/quic_cid.o + src/quic_rx.o src/quic_tx.o src/quic_cid.o src/quic_retry.o endif ifneq ($(USE_QUIC_OPENSSL_COMPAT),) diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h index 60d3840e2c..a1125fa290 100644 --- a/include/haproxy/quic_conn-t.h +++ b/include/haproxy/quic_conn-t.h @@ -88,8 +88,6 @@ typedef unsigned long long ull; #define QUIC_TOKEN_FMT_RETRY 0x9c /* Format for token sent for new connections after a Retry token was sent */ #define QUIC_TOKEN_FMT_NEW 0xb7 -/* Salt length used to derive retry token secret */ -#define QUIC_RETRY_TOKEN_SALTLEN 16 /* bytes */ /* Retry token duration */ #define QUIC_RETRY_DURATION_SEC 10 /* Default Retry threshold */ diff --git a/include/haproxy/quic_retry.h b/include/haproxy/quic_retry.h new file mode 100644 index 0000000000..d31be02eef --- /dev/null +++ b/include/haproxy/quic_retry.h @@ -0,0 +1,33 @@ +#ifndef _HAPROXY_QUIC_RETRY_H +#define _HAPROXY_QUIC_RETRY_H + +#ifdef USE_QUIC +#ifndef USE_OPENSSL +#error "Must define USE_OPENSSL" +#endif + +#include +#include + +#include +#include +#include + +struct listener; + +int quic_generate_retry_token(unsigned char *token, size_t len, + const uint32_t version, + const struct quic_cid *odcid, + const struct quic_cid *dcid, + struct sockaddr_storage *addr); +int parse_retry_token(struct quic_conn *qc, + const unsigned char *token, const unsigned char *end, + struct quic_cid *odcid); +int quic_retry_token_check(struct quic_rx_packet *pkt, + struct quic_dgram *dgram, + struct listener *l, + struct quic_conn *qc, + struct quic_cid *odcid); + +#endif /* USE_QUIC */ +#endif /* _HAPROXY_QUIC_RETRY_H */ diff --git a/include/haproxy/quic_tx.h b/include/haproxy/quic_tx.h index b81fa776ef..e556cce834 100644 --- a/include/haproxy/quic_tx.h +++ b/include/haproxy/quic_tx.h @@ -40,10 +40,6 @@ int qc_dgrams_retransmit(struct quic_conn *qc); int qc_notify_send(struct quic_conn *qc); void qc_prep_hdshk_fast_retrans(struct quic_conn *qc, struct list *ifrms, struct list *hfrms); -int quic_generate_retry_token_aad(unsigned char *aad, - uint32_t version, - const struct quic_cid *scid, - const struct sockaddr_storage *addr); int send_retry(int fd, struct sockaddr_storage *addr, struct quic_rx_packet *pkt, const struct quic_version *qv); int send_stateless_reset(struct listener *l, struct sockaddr_storage *dstaddr, diff --git a/src/quic_retry.c b/src/quic_retry.c new file mode 100644 index 0000000000..1c58e5e835 --- /dev/null +++ b/src/quic_retry.c @@ -0,0 +1,320 @@ +#include + +#include +#include +#include +#include +#include +#include + +#define TRACE_SOURCE &trace_quic + +/* Salt length used to derive retry token secret */ +#define QUIC_RETRY_TOKEN_SALTLEN 16 /* bytes */ + +/* Copy socket address data into buffer. + * This is the responsibility of the caller to check the output buffer is big + * enough to contain these socket address data. + * Return the number of bytes copied. + */ +static inline size_t quic_saddr_cpy(unsigned char *buf, + const struct sockaddr_storage *saddr) +{ + void *port, *addr; + unsigned char *p; + size_t port_len, addr_len; + + p = buf; + if (saddr->ss_family == AF_INET6) { + port = &((struct sockaddr_in6 *)saddr)->sin6_port; + addr = &((struct sockaddr_in6 *)saddr)->sin6_addr; + port_len = sizeof ((struct sockaddr_in6 *)saddr)->sin6_port; + addr_len = sizeof ((struct sockaddr_in6 *)saddr)->sin6_addr; + } + else { + port = &((struct sockaddr_in *)saddr)->sin_port; + addr = &((struct sockaddr_in *)saddr)->sin_addr; + port_len = sizeof ((struct sockaddr_in *)saddr)->sin_port; + addr_len = sizeof ((struct sockaddr_in *)saddr)->sin_addr; + } + memcpy(p, port, port_len); + p += port_len; + memcpy(p, addr, addr_len); + p += addr_len; + + return p - buf; +} + + +/* QUIC server only function. + * Add AAD to buffer from connection ID and socket address. + * This is the responsibility of the caller to check size is big enough + * to contain these data. + * Return the number of bytes copied to . + */ +static int quic_generate_retry_token_aad(unsigned char *aad, + uint32_t version, + const struct quic_cid *cid, + const struct sockaddr_storage *addr) +{ + unsigned char *p; + + p = aad; + *(uint32_t *)p = htonl(version); + p += sizeof version; + p += quic_saddr_cpy(p, addr); + memcpy(p, cid->data, cid->len); + p += cid->len; + + return p - aad; +} + +/* QUIC server only function. + * Generate the token to be used in Retry packets. The token is written to + * with as length. is the original destination connection + * ID and is our side destination connection ID (or client source + * connection ID). + * Returns the length of the encoded token or 0 on error. + */ +int quic_generate_retry_token(unsigned char *token, size_t len, + const uint32_t version, + const struct quic_cid *odcid, + const struct quic_cid *dcid, + struct sockaddr_storage *addr) +{ + int ret = 0; + unsigned char *p; + unsigned char aad[sizeof(uint32_t) + sizeof(in_port_t) + + sizeof(struct in6_addr) + QUIC_CID_MAXLEN]; + size_t aadlen; + unsigned char salt[QUIC_RETRY_TOKEN_SALTLEN]; + unsigned char key[QUIC_TLS_KEY_LEN]; + unsigned char iv[QUIC_TLS_IV_LEN]; + const unsigned char *sec = global.cluster_secret; + size_t seclen = sizeof global.cluster_secret; + EVP_CIPHER_CTX *ctx = NULL; + const EVP_CIPHER *aead = EVP_aes_128_gcm(); + uint32_t timestamp = (uint32_t)date.tv_sec; + + TRACE_ENTER(QUIC_EV_CONN_TXPKT); + + /* The token is made of the token format byte, the ODCID prefixed by its one byte + * length, the creation timestamp, an AEAD TAG, and finally + * the random bytes used to derive the secret to encrypt the token. + */ + if (1 + odcid->len + 1 + sizeof(timestamp) + QUIC_TLS_TAG_LEN + QUIC_RETRY_TOKEN_SALTLEN > len) + goto err; + + aadlen = quic_generate_retry_token_aad(aad, version, dcid, addr); + /* TODO: RAND_bytes() should be replaced */ + if (RAND_bytes(salt, sizeof salt) != 1) { + TRACE_ERROR("RAND_bytes()", QUIC_EV_CONN_TXPKT); + goto err; + } + + if (!quic_tls_derive_retry_token_secret(EVP_sha256(), key, sizeof key, iv, sizeof iv, + salt, sizeof salt, sec, seclen)) { + TRACE_ERROR("quic_tls_derive_retry_token_secret() failed", QUIC_EV_CONN_TXPKT); + goto err; + } + + if (!quic_tls_tx_ctx_init(&ctx, aead, key)) { + TRACE_ERROR("quic_tls_tx_ctx_init() failed", QUIC_EV_CONN_TXPKT); + goto err; + } + + /* Token build */ + p = token; + *p++ = QUIC_TOKEN_FMT_RETRY, + *p++ = odcid->len; + memcpy(p, odcid->data, odcid->len); + p += odcid->len; + write_u32(p, htonl(timestamp)); + p += sizeof timestamp; + + /* Do not encrypt the QUIC_TOKEN_FMT_RETRY byte */ + if (!quic_tls_encrypt(token + 1, p - token - 1, aad, aadlen, ctx, aead, iv)) { + TRACE_ERROR("quic_tls_encrypt() failed", QUIC_EV_CONN_TXPKT); + goto err; + } + + p += QUIC_TLS_TAG_LEN; + memcpy(p, salt, sizeof salt); + p += sizeof salt; + EVP_CIPHER_CTX_free(ctx); + + ret = p - token; + leave: + TRACE_LEAVE(QUIC_EV_CONN_TXPKT); + return ret; + + err: + if (ctx) + EVP_CIPHER_CTX_free(ctx); + goto leave; +} + +/* Parse the Retry token from buffer with a pointer to + * one byte past the end of this buffer. This will extract the ODCID + * which will be stored into + * + * Returns 0 on success else non-zero. + */ +int parse_retry_token(struct quic_conn *qc, + const unsigned char *token, const unsigned char *end, + struct quic_cid *odcid) +{ + int ret = 0; + uint64_t odcid_len; + uint32_t timestamp; + uint32_t now_sec = (uint32_t)date.tv_sec; + + TRACE_ENTER(QUIC_EV_CONN_LPKT, qc); + + if (!quic_dec_int(&odcid_len, &token, end)) { + TRACE_ERROR("quic_dec_int() error", QUIC_EV_CONN_LPKT, qc); + goto leave; + } + + /* RFC 9000 7.2. Negotiating Connection IDs: + * When an Initial packet is sent by a client that has not previously + * received an Initial or Retry packet from the server, the client + * populates the Destination Connection ID field with an unpredictable + * value. This Destination Connection ID MUST be at least 8 bytes in length. + */ + if (odcid_len < QUIC_ODCID_MINLEN || odcid_len > QUIC_CID_MAXLEN) { + TRACE_ERROR("wrong ODCID length", QUIC_EV_CONN_LPKT, qc); + goto leave; + } + + if (end - token < odcid_len + sizeof timestamp) { + TRACE_ERROR("too long ODCID length", QUIC_EV_CONN_LPKT, qc); + goto leave; + } + + timestamp = ntohl(read_u32(token + odcid_len)); + /* check if elapsed time is +/- QUIC_RETRY_DURATION_SEC + * to tolerate token generator is not perfectly time synced + */ + if ((uint32_t)(now_sec - timestamp) > QUIC_RETRY_DURATION_SEC && + (uint32_t)(timestamp - now_sec) > QUIC_RETRY_DURATION_SEC) { + TRACE_ERROR("token has expired", QUIC_EV_CONN_LPKT, qc); + goto leave; + } + + ret = 1; + memcpy(odcid->data, token, odcid_len); + odcid->len = odcid_len; + leave: + TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc); + return !ret; +} + +/* QUIC server only function. + * + * Check the validity of the Retry token from Initial packet . is + * the UDP datagram containing and is the listener instance on which + * it was received. If the token is valid, the ODCID of QUIC connection + * will be put into . is used to retrieve the QUIC version needed + * to validate the token but it can be NULL : in this case the version will be + * retrieved from the packet. + * + * Return 1 if succeeded, 0 if not. + */ + +int quic_retry_token_check(struct quic_rx_packet *pkt, + struct quic_dgram *dgram, + struct listener *l, + struct quic_conn *qc, + struct quic_cid *odcid) +{ + struct proxy *prx; + struct quic_counters *prx_counters; + int ret = 0; + unsigned char *token = pkt->token; + const uint64_t tokenlen = pkt->token_len; + unsigned char buf[128]; + unsigned char aad[sizeof(uint32_t) + QUIC_CID_MAXLEN + + sizeof(in_port_t) + sizeof(struct in6_addr)]; + size_t aadlen; + const unsigned char *salt; + unsigned char key[QUIC_TLS_KEY_LEN]; + unsigned char iv[QUIC_TLS_IV_LEN]; + const unsigned char *sec = global.cluster_secret; + size_t seclen = sizeof global.cluster_secret; + EVP_CIPHER_CTX *ctx = NULL; + const EVP_CIPHER *aead = EVP_aes_128_gcm(); + const struct quic_version *qv = qc ? qc->original_version : + pkt->version; + + TRACE_ENTER(QUIC_EV_CONN_LPKT, qc); + + /* The caller must ensure this. */ + BUG_ON(!pkt->token_len); + + prx = l->bind_conf->frontend; + prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module); + + if (*pkt->token != QUIC_TOKEN_FMT_RETRY) { + /* TODO: New token check */ + TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc, NULL, NULL, pkt->version); + goto leave; + } + + if (sizeof buf < tokenlen) { + TRACE_ERROR("too short buffer", QUIC_EV_CONN_LPKT, qc); + goto err; + } + + /* The token is made of the token format byte, the ODCID prefixed by its one byte + * length, the creation timestamp, an AEAD TAG, and finally + * the random bytes used to derive the secret to encrypt the token. + */ + if (tokenlen < 2 + QUIC_ODCID_MINLEN + sizeof(uint32_t) + QUIC_TLS_TAG_LEN + QUIC_RETRY_TOKEN_SALTLEN || + tokenlen > 2 + QUIC_CID_MAXLEN + sizeof(uint32_t) + QUIC_TLS_TAG_LEN + QUIC_RETRY_TOKEN_SALTLEN) { + TRACE_ERROR("invalid token length", QUIC_EV_CONN_LPKT, qc); + goto err; + } + + aadlen = quic_generate_retry_token_aad(aad, qv->num, &pkt->scid, &dgram->saddr); + salt = token + tokenlen - QUIC_RETRY_TOKEN_SALTLEN; + if (!quic_tls_derive_retry_token_secret(EVP_sha256(), key, sizeof key, iv, sizeof iv, + salt, QUIC_RETRY_TOKEN_SALTLEN, sec, seclen)) { + TRACE_ERROR("Could not derive retry secret", QUIC_EV_CONN_LPKT, qc); + goto err; + } + + if (!quic_tls_rx_ctx_init(&ctx, aead, key)) { + TRACE_ERROR("quic_tls_rx_ctx_init() failed", QUIC_EV_CONN_LPKT, qc); + goto err; + } + + /* The token is prefixed by a one-byte length format which is not ciphered. */ + if (!quic_tls_decrypt2(buf, token + 1, tokenlen - QUIC_RETRY_TOKEN_SALTLEN - 1, aad, aadlen, + ctx, aead, key, iv)) { + TRACE_ERROR("Could not decrypt retry token", QUIC_EV_CONN_LPKT, qc); + goto err; + } + + if (parse_retry_token(qc, buf, buf + tokenlen - QUIC_RETRY_TOKEN_SALTLEN - 1, odcid)) { + TRACE_ERROR("Error during Initial token parsing", QUIC_EV_CONN_LPKT, qc); + goto err; + } + + EVP_CIPHER_CTX_free(ctx); + + ret = 1; + HA_ATOMIC_INC(&prx_counters->retry_validated); + + leave: + TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc); + return ret; + + err: + HA_ATOMIC_INC(&prx_counters->retry_error); + if (ctx) + EVP_CIPHER_CTX_free(ctx); + goto leave; +} + + diff --git a/src/quic_rx.c b/src/quic_rx.c index 0c313446c3..0f0c9879a7 100644 --- a/src/quic_rx.c +++ b/src/quic_rx.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1350,62 +1351,6 @@ int qc_treat_rx_pkts(struct quic_conn *qc) return ret; } -/* Parse the Retry token from buffer with a pointer to - * one byte past the end of this buffer. This will extract the ODCID - * which will be stored into - * - * Returns 0 on success else non-zero. - */ -static int parse_retry_token(struct quic_conn *qc, - const unsigned char *token, const unsigned char *end, - struct quic_cid *odcid) -{ - int ret = 0; - uint64_t odcid_len; - uint32_t timestamp; - uint32_t now_sec = (uint32_t)date.tv_sec; - - TRACE_ENTER(QUIC_EV_CONN_LPKT, qc); - - if (!quic_dec_int(&odcid_len, &token, end)) { - TRACE_ERROR("quic_dec_int() error", QUIC_EV_CONN_LPKT, qc); - goto leave; - } - - /* RFC 9000 7.2. Negotiating Connection IDs: - * When an Initial packet is sent by a client that has not previously - * received an Initial or Retry packet from the server, the client - * populates the Destination Connection ID field with an unpredictable - * value. This Destination Connection ID MUST be at least 8 bytes in length. - */ - if (odcid_len < QUIC_ODCID_MINLEN || odcid_len > QUIC_CID_MAXLEN) { - TRACE_ERROR("wrong ODCID length", QUIC_EV_CONN_LPKT, qc); - goto leave; - } - - if (end - token < odcid_len + sizeof timestamp) { - TRACE_ERROR("too long ODCID length", QUIC_EV_CONN_LPKT, qc); - goto leave; - } - - timestamp = ntohl(read_u32(token + odcid_len)); - /* check if elapsed time is +/- QUIC_RETRY_DURATION_SEC - * to tolerate token generator is not perfectly time synced - */ - if ((uint32_t)(now_sec - timestamp) > QUIC_RETRY_DURATION_SEC && - (uint32_t)(timestamp - now_sec) > QUIC_RETRY_DURATION_SEC) { - TRACE_ERROR("token has expired", QUIC_EV_CONN_LPKT, qc); - goto leave; - } - - ret = 1; - memcpy(odcid->data, token, odcid_len); - odcid->len = odcid_len; - leave: - TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc); - return !ret; -} - /* Parse into a long header located at <*pos> position, begin a pointer to the end * past one byte of this buffer. */ @@ -1650,113 +1595,6 @@ int qc_parse_hd_form(struct quic_rx_packet *pkt, return ret; } -/* QUIC server only function. - * - * Check the validity of the Retry token from Initial packet . is - * the UDP datagram containing and is the listener instance on which - * it was received. If the token is valid, the ODCID of QUIC connection - * will be put into . is used to retrieve the QUIC version needed - * to validate the token but it can be NULL : in this case the version will be - * retrieved from the packet. - * - * Return 1 if succeeded, 0 if not. - */ - -static int quic_retry_token_check(struct quic_rx_packet *pkt, - struct quic_dgram *dgram, - struct listener *l, - struct quic_conn *qc, - struct quic_cid *odcid) -{ - struct proxy *prx; - struct quic_counters *prx_counters; - int ret = 0; - unsigned char *token = pkt->token; - const uint64_t tokenlen = pkt->token_len; - unsigned char buf[128]; - unsigned char aad[sizeof(uint32_t) + QUIC_CID_MAXLEN + - sizeof(in_port_t) + sizeof(struct in6_addr)]; - size_t aadlen; - const unsigned char *salt; - unsigned char key[QUIC_TLS_KEY_LEN]; - unsigned char iv[QUIC_TLS_IV_LEN]; - const unsigned char *sec = global.cluster_secret; - size_t seclen = sizeof global.cluster_secret; - EVP_CIPHER_CTX *ctx = NULL; - const EVP_CIPHER *aead = EVP_aes_128_gcm(); - const struct quic_version *qv = qc ? qc->original_version : - pkt->version; - - TRACE_ENTER(QUIC_EV_CONN_LPKT, qc); - - /* The caller must ensure this. */ - BUG_ON(!pkt->token_len); - - prx = l->bind_conf->frontend; - prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module); - - if (*pkt->token != QUIC_TOKEN_FMT_RETRY) { - /* TODO: New token check */ - TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc, NULL, NULL, pkt->version); - goto leave; - } - - if (sizeof buf < tokenlen) { - TRACE_ERROR("too short buffer", QUIC_EV_CONN_LPKT, qc); - goto err; - } - - /* The token is made of the token format byte, the ODCID prefixed by its one byte - * length, the creation timestamp, an AEAD TAG, and finally - * the random bytes used to derive the secret to encrypt the token. - */ - if (tokenlen < 2 + QUIC_ODCID_MINLEN + sizeof(uint32_t) + QUIC_TLS_TAG_LEN + QUIC_RETRY_TOKEN_SALTLEN || - tokenlen > 2 + QUIC_CID_MAXLEN + sizeof(uint32_t) + QUIC_TLS_TAG_LEN + QUIC_RETRY_TOKEN_SALTLEN) { - TRACE_ERROR("invalid token length", QUIC_EV_CONN_LPKT, qc); - goto err; - } - - aadlen = quic_generate_retry_token_aad(aad, qv->num, &pkt->scid, &dgram->saddr); - salt = token + tokenlen - QUIC_RETRY_TOKEN_SALTLEN; - if (!quic_tls_derive_retry_token_secret(EVP_sha256(), key, sizeof key, iv, sizeof iv, - salt, QUIC_RETRY_TOKEN_SALTLEN, sec, seclen)) { - TRACE_ERROR("Could not derive retry secret", QUIC_EV_CONN_LPKT, qc); - goto err; - } - - if (!quic_tls_rx_ctx_init(&ctx, aead, key)) { - TRACE_ERROR("quic_tls_rx_ctx_init() failed", QUIC_EV_CONN_LPKT, qc); - goto err; - } - - /* The token is prefixed by a one-byte length format which is not ciphered. */ - if (!quic_tls_decrypt2(buf, token + 1, tokenlen - QUIC_RETRY_TOKEN_SALTLEN - 1, aad, aadlen, - ctx, aead, key, iv)) { - TRACE_ERROR("Could not decrypt retry token", QUIC_EV_CONN_LPKT, qc); - goto err; - } - - if (parse_retry_token(qc, buf, buf + tokenlen - QUIC_RETRY_TOKEN_SALTLEN - 1, odcid)) { - TRACE_ERROR("Error during Initial token parsing", QUIC_EV_CONN_LPKT, qc); - goto err; - } - - EVP_CIPHER_CTX_free(ctx); - - ret = 1; - HA_ATOMIC_INC(&prx_counters->retry_validated); - - leave: - TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc); - return ret; - - err: - HA_ATOMIC_INC(&prx_counters->retry_error); - if (ctx) - EVP_CIPHER_CTX_free(ctx); - goto leave; -} - /* Check that all the bytes between included and address * excluded are null. This is the responsibility of the caller to * check that there is at least one byte between end . diff --git a/src/quic_tx.c b/src/quic_tx.c index 57a0934459..4af91b2a84 100644 --- a/src/quic_tx.c +++ b/src/quic_tx.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -1457,148 +1458,6 @@ int send_stateless_reset(struct listener *l, struct sockaddr_storage *dstaddr, return ret; } -/* Copy socket address data into buffer. - * This is the responsibility of the caller to check the output buffer is big - * enough to contain these socket address data. - * Return the number of bytes copied. - */ -static inline size_t quic_saddr_cpy(unsigned char *buf, - const struct sockaddr_storage *saddr) -{ - void *port, *addr; - unsigned char *p; - size_t port_len, addr_len; - - p = buf; - if (saddr->ss_family == AF_INET6) { - port = &((struct sockaddr_in6 *)saddr)->sin6_port; - addr = &((struct sockaddr_in6 *)saddr)->sin6_addr; - port_len = sizeof ((struct sockaddr_in6 *)saddr)->sin6_port; - addr_len = sizeof ((struct sockaddr_in6 *)saddr)->sin6_addr; - } - else { - port = &((struct sockaddr_in *)saddr)->sin_port; - addr = &((struct sockaddr_in *)saddr)->sin_addr; - port_len = sizeof ((struct sockaddr_in *)saddr)->sin_port; - addr_len = sizeof ((struct sockaddr_in *)saddr)->sin_addr; - } - memcpy(p, port, port_len); - p += port_len; - memcpy(p, addr, addr_len); - p += addr_len; - - return p - buf; -} - - -/* QUIC server only function. - * Add AAD to buffer from connection ID and socket address. - * This is the responsibility of the caller to check size is big enough - * to contain these data. - * Return the number of bytes copied to . - */ -int quic_generate_retry_token_aad(unsigned char *aad, - uint32_t version, - const struct quic_cid *cid, - const struct sockaddr_storage *addr) -{ - unsigned char *p; - - p = aad; - *(uint32_t *)p = htonl(version); - p += sizeof version; - p += quic_saddr_cpy(p, addr); - memcpy(p, cid->data, cid->len); - p += cid->len; - - return p - aad; -} - -/* QUIC server only function. - * Generate the token to be used in Retry packets. The token is written to - * with as length. is the original destination connection - * ID and is our side destination connection ID (or client source - * connection ID). - * Returns the length of the encoded token or 0 on error. - */ -static int quic_generate_retry_token(unsigned char *token, size_t len, - const uint32_t version, - const struct quic_cid *odcid, - const struct quic_cid *dcid, - struct sockaddr_storage *addr) -{ - int ret = 0; - unsigned char *p; - unsigned char aad[sizeof(uint32_t) + sizeof(in_port_t) + - sizeof(struct in6_addr) + QUIC_CID_MAXLEN]; - size_t aadlen; - unsigned char salt[QUIC_RETRY_TOKEN_SALTLEN]; - unsigned char key[QUIC_TLS_KEY_LEN]; - unsigned char iv[QUIC_TLS_IV_LEN]; - const unsigned char *sec = global.cluster_secret; - size_t seclen = sizeof global.cluster_secret; - EVP_CIPHER_CTX *ctx = NULL; - const EVP_CIPHER *aead = EVP_aes_128_gcm(); - uint32_t timestamp = (uint32_t)date.tv_sec; - - TRACE_ENTER(QUIC_EV_CONN_TXPKT); - - /* The token is made of the token format byte, the ODCID prefixed by its one byte - * length, the creation timestamp, an AEAD TAG, and finally - * the random bytes used to derive the secret to encrypt the token. - */ - if (1 + odcid->len + 1 + sizeof(timestamp) + QUIC_TLS_TAG_LEN + QUIC_RETRY_TOKEN_SALTLEN > len) - goto err; - - aadlen = quic_generate_retry_token_aad(aad, version, dcid, addr); - /* TODO: RAND_bytes() should be replaced */ - if (RAND_bytes(salt, sizeof salt) != 1) { - TRACE_ERROR("RAND_bytes()", QUIC_EV_CONN_TXPKT); - goto err; - } - - if (!quic_tls_derive_retry_token_secret(EVP_sha256(), key, sizeof key, iv, sizeof iv, - salt, sizeof salt, sec, seclen)) { - TRACE_ERROR("quic_tls_derive_retry_token_secret() failed", QUIC_EV_CONN_TXPKT); - goto err; - } - - if (!quic_tls_tx_ctx_init(&ctx, aead, key)) { - TRACE_ERROR("quic_tls_tx_ctx_init() failed", QUIC_EV_CONN_TXPKT); - goto err; - } - - /* Token build */ - p = token; - *p++ = QUIC_TOKEN_FMT_RETRY, - *p++ = odcid->len; - memcpy(p, odcid->data, odcid->len); - p += odcid->len; - write_u32(p, htonl(timestamp)); - p += sizeof timestamp; - - /* Do not encrypt the QUIC_TOKEN_FMT_RETRY byte */ - if (!quic_tls_encrypt(token + 1, p - token - 1, aad, aadlen, ctx, aead, iv)) { - TRACE_ERROR("quic_tls_encrypt() failed", QUIC_EV_CONN_TXPKT); - goto err; - } - - p += QUIC_TLS_TAG_LEN; - memcpy(p, salt, sizeof salt); - p += sizeof salt; - EVP_CIPHER_CTX_free(ctx); - - ret = p - token; - leave: - TRACE_LEAVE(QUIC_EV_CONN_TXPKT); - return ret; - - err: - if (ctx) - EVP_CIPHER_CTX_free(ctx); - goto leave; -} - /* Return the long packet type matching with version and */ static inline int quic_pkt_type(int type, uint32_t version) {