From a50775d4e6dde6b79c65a5d73bef9bb5eb9389c8 Mon Sep 17 00:00:00 2001 From: Frederic Lecaille Date: Thu, 29 Aug 2024 11:32:14 +0200 Subject: [PATCH] WIP: O-RTT securing --- Makefile | 3 +- include/haproxy/quic_conn-t.h | 2 + include/haproxy/quic_frame-t.h | 3 +- include/haproxy/quic_frame.h | 2 +- include/haproxy/quic_ssl.h | 9 ++ include/haproxy/quic_tls.h | 32 ++++++ include/haproxy/quic_token.h | 49 +++++++++ src/quic_conn.c | 46 +++++++- src/quic_frame.c | 6 +- src/quic_retry.c | 8 +- src/quic_rx.c | 62 ++++++++++- src/quic_ssl.c | 18 ++- src/quic_tls.c | 47 ++++++-- src/quic_token.c | 193 +++++++++++++++++++++++++++++++++ src/quic_trace.c | 5 + 15 files changed, 458 insertions(+), 27 deletions(-) create mode 100644 include/haproxy/quic_token.h create mode 100644 src/quic_token.c diff --git a/Makefile b/Makefile index ff1810a87..238a50377 100644 --- a/Makefile +++ b/Makefile @@ -651,7 +651,8 @@ OPTIONS_OBJS += src/quic_rx.o src/mux_quic.o src/h3.o src/quic_tx.o \ src/quic_cc_nocc.o src/qpack-dec.o src/quic_cc.o \ src/cfgparse-quic.o src/qmux_trace.o src/qpack-enc.o \ src/qpack-tbl.o src/h3_stats.o src/quic_stats.o \ - src/quic_fctl.o src/cbuf.o src/quic_rules.o + src/quic_fctl.o src/cbuf.o src/quic_rules.o \ + src/quic_token.o endif ifneq ($(USE_QUIC_OPENSSL_COMPAT:0=),) diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h index 8a4ed58be..a85606ffc 100644 --- a/include/haproxy/quic_conn-t.h +++ b/include/haproxy/quic_conn-t.h @@ -445,6 +445,8 @@ struct quic_conn_closed { #define QUIC_FL_CONN_IPKTNS_DCD (1U << 15) /* Initial packet number space discarded */ #define QUIC_FL_CONN_HPKTNS_DCD (1U << 16) /* Handshake packet number space discarded */ #define QUIC_FL_CONN_PEER_VALIDATED_ADDR (1U << 17) /* Peer address is considered as validated for this connection. */ +#define QUIC_FL_CONN_NO_TOKEN_RCVD (1U << 18) /* Client dit not send any token */ +#define QUIC_FL_CONN_SEND_RETRY (1U << 19) /* A send retry packet must be sent */ /* gap here */ #define QUIC_FL_CONN_TO_KILL (1U << 24) /* Unusable connection, to be killed */ #define QUIC_FL_CONN_TX_TP_RECEIVED (1U << 25) /* Peer transport parameters have been received (used for the transmitting part) */ diff --git a/include/haproxy/quic_frame-t.h b/include/haproxy/quic_frame-t.h index 5e91f9320..fe80f2793 100644 --- a/include/haproxy/quic_frame-t.h +++ b/include/haproxy/quic_frame-t.h @@ -33,6 +33,7 @@ #include #include #include +#include extern struct pool_head *pool_head_quic_frame; extern struct pool_head *pool_head_qf_crypto; @@ -154,7 +155,7 @@ struct qf_crypto { struct qf_new_token { uint64_t len; - const unsigned char *data; + unsigned char data[QUIC_TOKEN_LEN]; }; struct qf_stream { diff --git a/include/haproxy/quic_frame.h b/include/haproxy/quic_frame.h index 90d6b213c..b93a93728 100644 --- a/include/haproxy/quic_frame.h +++ b/include/haproxy/quic_frame.h @@ -91,7 +91,7 @@ static inline size_t qc_frm_len(struct quic_frame *frm) } case QUIC_FT_NEW_TOKEN: { struct qf_new_token *f = &frm->new_token; - len += 1 + quic_int_getsize(f->len) + f->len; + len += sizeof(f->data); break; } case QUIC_FT_STREAM_8 ... QUIC_FT_STREAM_F: { diff --git a/include/haproxy/quic_ssl.h b/include/haproxy/quic_ssl.h index a84f5fffc..3e3d49185 100644 --- a/include/haproxy/quic_ssl.h +++ b/include/haproxy/quic_ssl.h @@ -47,5 +47,14 @@ static inline void qc_free_ssl_sock_ctx(struct ssl_sock_ctx **ctx) *ctx = NULL; } +static inline int qc_ssl_eary_data_accepted(const SSL *ssl) +{ +#if defined(OPENSSL_IS_AWSLC) + return SSL_early_data_accepted(ssl); +#else + return SSL_get_early_data_status(ssl) == SSL_EARLY_DATA_ACCEPTED; +#endif +} + #endif /* USE_QUIC */ #endif /* _HAPROXY_QUIC_SSL_H */ diff --git a/include/haproxy/quic_tls.h b/include/haproxy/quic_tls.h index 757b78fb4..62faedacc 100644 --- a/include/haproxy/quic_tls.h +++ b/include/haproxy/quic_tls.h @@ -90,6 +90,12 @@ int quic_tls_derive_retry_token_secret(const EVP_MD *md, const unsigned char *salt, size_t saltlen, const unsigned char *secret, size_t secretlen); +int quic_tls_derive_token_secret(const EVP_MD *md, + unsigned char *key, size_t keylen, + unsigned char *iv, size_t ivlen, + const unsigned char *salt, size_t saltlen, + const unsigned char *secret, size_t secretlen); + int quic_hkdf_expand(const EVP_MD *md, unsigned char *buf, size_t buflen, const unsigned char *key, size_t keylen, @@ -469,6 +475,32 @@ static inline char quic_packet_type_enc_level_char(int packet_type) } } +#if defined(OPENSSL_IS_AWSLC) +static inline const char *quic_ssl_early_data_status_str(const SSL *ssl) +{ + if (SSL_early_data_accepted(ssl)) + return "ACCEPTED"; + else + return "UNKNOWN"; +} +#else +static inline const char *quic_ssl_early_data_status_str(const SSL *ssl) +{ + int early_data_status = SSL_get_early_data_status(ssl); + + switch (early_data_status) { + case SSL_EARLY_DATA_ACCEPTED: + return "ACCEPTED"; + case SSL_EARLY_DATA_REJECTED: + return "REJECTED"; + case SSL_EARLY_DATA_NOT_SENT: + return "NOT_SENT"; + default: + return "UNKNOWN"; + } +} +#endif + /* Initialize a QUIC packet number space. * Never fails. */ diff --git a/include/haproxy/quic_token.h b/include/haproxy/quic_token.h new file mode 100644 index 000000000..8960ccd81 --- /dev/null +++ b/include/haproxy/quic_token.h @@ -0,0 +1,49 @@ +/* + * include/haproxy/quic_token.h + * This file contains definition for QUIC tokens (provided by NEW_TOKEN). + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, version 2.1 + * exclusively. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _PROTO_QUIC_TOKEN_H +#define _PROTO_QUIC_TOKEN_H + +#include +#include +#include +#include + +#define QUIC_TOKEN_RAND_LEN 16 +/* The size of QUIC token as provided by NEW_TOKEN frame in bytes: + * one byte as format identifier, sizeof(uint32_t) bytes for the timestamp, + * QUIC_TLS_TAG_LEN bytes for the AEAD TAG and QUIC_TOKEN_RAND_LEN bytes + * for the random data part which are used to derive a token secret in + * addition to the cluster secret (global.cluster_secret). + */ +#define QUIC_TOKEN_LEN (1 + sizeof(uint32_t) + QUIC_TLS_TAG_LEN + QUIC_TOKEN_RAND_LEN) + +/* RFC 9001 4.6. O-RTT + * TLS13 sets a limit of seven days on the time between the original + * connection and any attempt to use 0-RTT. + */ +#define QUIC_TOKEN_DURATION_SEC (7 * 24 * 3600) // 7 days in seconds + +int quic_generate_token(unsigned char *token, size_t len, + struct sockaddr_storage *addr); +int quic_token_check(struct quic_rx_packet *pkt, struct quic_dgram *dgram, + struct quic_conn *qc); + +#endif /* _PROTO_QUIC_TOKEN_H */ + diff --git a/src/quic_conn.c b/src/quic_conn.c index 80401fe27..0c24f8f07 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -479,6 +480,20 @@ int quic_build_post_handshake_frames(struct quic_conn *qc) } LIST_APPEND(&frm_list, &frm->list); + + frm = qc_frm_alloc(QUIC_FT_NEW_TOKEN); + if (!frm) { + TRACE_ERROR("frame allocation error", QUIC_EV_CONN_IO_CB, qc); + goto leave; + } + + if (!quic_generate_token(frm->new_token.data, sizeof(frm->new_token.data), &qc->peer_addr)) { + TRACE_ERROR("token generation failed", QUIC_EV_CONN_IO_CB, qc); + goto leave; + } + + frm->new_token.len = sizeof(frm->new_token.data); + LIST_APPEND(&frm_list, &frm->list); } /* Initialize connection IDs minus one: there is @@ -755,6 +770,11 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state) st = qc->state; TRACE_PROTO("connection state", QUIC_EV_CONN_IO_CB, qc, &st); + if (qc->flags & QUIC_FL_CONN_TO_KILL) { + TRACE_DEVEL("connection to be killed", QUIC_EV_CONN_PHPKTS, qc); + goto out; + } + /* TASK_HEAVY is set when received CRYPTO data have to be handled. */ if (HA_ATOMIC_LOAD(&tl->state) & TASK_HEAVY) { qc_ssl_provide_all_quic_data(qc, qc->xprt_ctx); @@ -862,7 +882,24 @@ struct task *quic_conn_io_cb(struct task *t, void *context, unsigned int state) quic_nictx_free(qc); } - if ((qc->flags & QUIC_FL_CONN_CLOSING) && qc->mux_state != QC_MUX_READY) { + if (qc->flags & QUIC_FL_CONN_SEND_RETRY) { + struct quic_counters *prx_counters; + struct proxy *prx = qc->li->bind_conf->frontend; + struct quic_rx_packet pkt = { + .scid = qc->dcid, + .dcid = qc->odcid, + }; + + prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module); + if (send_retry(qc->li->rx.fd, &qc->peer_addr, &pkt, qc->original_version)) { + TRACE_ERROR("Error during Retry generation", + QUIC_EV_CONN_LPKT, NULL, NULL, NULL, qc->original_version); + } + else + HA_ATOMIC_INC(&prx_counters->retry_sent); + } + + if ((qc->flags & (QUIC_FL_CONN_CLOSING|QUIC_FL_CONN_TO_KILL)) && qc->mux_state != QC_MUX_READY) { quic_conn_release(qc); qc = NULL; } @@ -972,8 +1009,8 @@ struct task *qc_process_timer(struct task *task, void *ctx, unsigned int state) * which is the first CID of this connection but a different internal representation used to build * NEW_CONNECTION_ID frames. This is the responsibility of the caller to insert * in the CIDs tree for this connection (qc->cids). - * is the token found to be used for this connection with as - * length. Endpoints addresses are specified via and . + * is a boolean denoting if a token was received for this connection. + * Endpoints addresses are specified via and . * Returns the connection if succeeded, NULL if not. */ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4, @@ -1080,6 +1117,9 @@ struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4, qc->prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module); qc->flags = QUIC_FL_CONN_LISTENER; + /* Mark this connection as having not received any token when 0-RTT is enabled. */ + if (l->bind_conf->ssl_conf.early_data && !token) + qc->flags |= QUIC_FL_CONN_NO_TOKEN_RCVD; qc->state = QUIC_HS_ST_SERVER_INITIAL; /* Copy the client original DCID. */ qc->odcid = *dcid; diff --git a/src/quic_frame.c b/src/quic_frame.c index 41309dbfe..3db56ee42 100644 --- a/src/quic_frame.c +++ b/src/quic_frame.c @@ -485,6 +485,7 @@ static int quic_build_new_token_frame(unsigned char **pos, const unsigned char * return 0; memcpy(*pos, new_token_frm->data, new_token_frm->len); + *pos += new_token_frm->len; return 1; } @@ -497,10 +498,11 @@ static int quic_parse_new_token_frame(struct quic_frame *frm, struct quic_conn * { struct qf_new_token *new_token_frm = &frm->new_token; - if (!quic_dec_int(&new_token_frm->len, pos, end) || end - *pos < new_token_frm->len) + if (!quic_dec_int(&new_token_frm->len, pos, end) || end - *pos < new_token_frm->len || + sizeof(new_token_frm->data) < new_token_frm->len) return 0; - new_token_frm->data = *pos; + memcpy(new_token_frm->data, *pos, new_token_frm->len); *pos += new_token_frm->len; return 1; diff --git a/src/quic_retry.c b/src/quic_retry.c index 2d6ea31ac..78ef88a76 100644 --- a/src/quic_retry.c +++ b/src/quic_retry.c @@ -258,17 +258,11 @@ int quic_retry_token_check(struct quic_rx_packet *pkt, TRACE_ENTER(QUIC_EV_CONN_LPKT, qc); /* The caller must ensure this. */ - BUG_ON(!pkt->token_len); + BUG_ON(!pkt->token_len || *pkt->token != QUIC_TOKEN_FMT_RETRY); 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; diff --git a/src/quic_rx.c b/src/quic_rx.c index 6e21958cc..82a4b5d89 100644 --- a/src/quic_rx.c +++ b/src/quic_rx.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -1522,6 +1523,47 @@ static inline int quic_padding_check(const unsigned char *pos, return pos == end; } +/* Validate the token, retry or not (provided by NEW_TOKEN) parsed into + * RX packet from datagram. + * Return 1 if succeded, 0 if not. + */ +static inline int quic_token_validate(struct quic_rx_packet *pkt, + struct quic_dgram *dgram, + struct listener *l, struct quic_conn *qc, + struct quic_cid *odcid) +{ + int ret = 0; + + TRACE_ENTER(QUIC_EV_CONN_LPKT, qc); + + switch (*pkt->token) { + case QUIC_TOKEN_FMT_RETRY: + ret = quic_retry_token_check(pkt, dgram, l, qc, odcid); + break; + case QUIC_TOKEN_FMT_NEW: + ret = quic_token_check(pkt, dgram, qc); + if (!ret) { + /* Fallback to a retry token in case of any error. */ + dgram->flags |= QUIC_DGRAM_FL_SEND_RETRY; + } + break; + default: + TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc, NULL, NULL, pkt->version); + break; + } + + if (!ret) + goto err; + + ret = 1; + leave: + TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc); + return ret; + err: + TRACE_DEVEL("leaving in error", QUIC_EV_CONN_LPKT, qc); + goto leave; +} + /* Find the associated connection to the packet or create a new one if * this is an Initial packet. is the datagram containing the packet and * is the listener instance on which it was received. @@ -1581,9 +1623,25 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt, } if (pkt->token_len) { - /* Validate the token only when connection is unknown. */ - if (!quic_retry_token_check(pkt, dgram, l, qc, &token_odcid)) + TRACE_PROTO("Initial with token", QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version); + /* Validate the token, retry or not only when connection is unknown. */ + if (!quic_token_validate(pkt, dgram, l, qc, &token_odcid)) { + if (dgram->flags & QUIC_DGRAM_FL_SEND_RETRY) { + if (send_retry(l->rx.fd, &dgram->saddr, pkt, pkt->version)) { + TRACE_ERROR("Error during Retry generation", + QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version); + } + else + HA_ATOMIC_INC(&prx_counters->retry_sent); + + goto out; + } + goto err; + } + } + else { + TRACE_PROTO("Initial without token", QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version); } if (!quic_init_exec_rules(l, dgram)) { diff --git a/src/quic_ssl.c b/src/quic_ssl.c index 3d3f8e77b..db46885b8 100644 --- a/src/quic_ssl.c +++ b/src/quic_ssl.c @@ -353,6 +353,21 @@ static int ha_quic_add_handshake_data(SSL *ssl, enum ssl_encryption_level_t leve TRACE_ENTER(QUIC_EV_CONN_ADDDATA, qc); + TRACE_PROTO("ha_quic_add_handshake_data() called", QUIC_EV_CONN_IO_CB, qc, NULL, ssl); + + /* Detect asap if some 0-RTT data were accepted for this connection. + * If this is the case and no token was provided, interrupt the useless + * secrets derivations. A Retry packet must be sent, and this connection + * must be killed. + * Note that QUIC_FL_CONN_NO_TOKEN_RCVD is possibly set only for when 0-RTT is + * enabled for the connection. + */ + if ((qc->flags & QUIC_FL_CONN_NO_TOKEN_RCVD) && qc_ssl_eary_data_accepted(ssl)) { + TRACE_PROTO("connection to be killed", QUIC_EV_CONN_ADDDATA, qc); + qc->flags |= QUIC_FL_CONN_TO_KILL|QUIC_FL_CONN_SEND_RETRY; + goto leave; + } + if (qc->flags & QUIC_FL_CONN_TO_KILL) { TRACE_PROTO("connection to be killed", QUIC_EV_CONN_ADDDATA, qc); goto out; @@ -533,9 +548,10 @@ static int qc_ssl_provide_quic_data(struct ncbuf *ncbuf, state = qc->state; if (state < QUIC_HS_ST_COMPLETE) { ssl_err = SSL_do_handshake(ctx->ssl); + TRACE_PROTO("SSL_do_handshake() called", QUIC_EV_CONN_IO_CB, qc, NULL, ctx->ssl); if (qc->flags & QUIC_FL_CONN_TO_KILL) { - TRACE_DEVEL("connection to be killed", QUIC_EV_CONN_IO_CB, qc); + TRACE_DEVEL("connection to be killed", QUIC_EV_CONN_IO_CB, qc, &state, ctx->ssl); goto leave; } diff --git a/src/quic_tls.c b/src/quic_tls.c index e1f5f2175..7a82d8720 100644 --- a/src/quic_tls.c +++ b/src/quic_tls.c @@ -950,27 +950,56 @@ int quic_tls_decrypt2(unsigned char *out, * with which is not pseudo-random. * Return 1 if succeeded, 0 if not. */ -int quic_tls_derive_retry_token_secret(const EVP_MD *md, - unsigned char *key, size_t keylen, - unsigned char *iv, size_t ivlen, - const unsigned char *salt, size_t saltlen, - const unsigned char *secret, size_t secretlen) +static inline int quic_do_tls_derive_retry_token_secret(const EVP_MD *md, unsigned char *key, size_t keylen, + unsigned char *iv, size_t ivlen, + const unsigned char *salt, size_t saltlen, + const unsigned char *secret, size_t secretlen, + const unsigned char *klabel, size_t klabellen, + const unsigned char *ivlabel, size_t ivlabellen) { unsigned char tmpkey[QUIC_TLS_KEY_LEN]; - const unsigned char key_label[] = "retry token key"; - const unsigned char iv_label[] = "retry token iv"; if (!quic_hkdf_extract(md, tmpkey, sizeof tmpkey, secret, secretlen, salt, saltlen) || !quic_hkdf_expand(md, key, keylen, tmpkey, sizeof tmpkey, - key_label, sizeof key_label - 1) || + klabel, klabellen) || !quic_hkdf_expand(md, iv, ivlen, tmpkey, sizeof tmpkey, - iv_label, sizeof iv_label - 1)) + ivlabel, ivlabellen)) return 0; return 1; } +int quic_tls_derive_retry_token_secret(const EVP_MD *md, + unsigned char *key, size_t keylen, + unsigned char *iv, size_t ivlen, + const unsigned char *salt, size_t saltlen, + const unsigned char *secret, size_t secretlen) +{ + const unsigned char key_label[] = "retry token key"; + const unsigned char iv_label[] = "retry token iv"; + + return quic_do_tls_derive_retry_token_secret(md, key, keylen, iv, ivlen, + salt, saltlen, secret, secretlen, + key_label, sizeof(key_label) - 1, + iv_label, sizeof(iv_label) -1); +} + +int quic_tls_derive_token_secret(const EVP_MD *md, + unsigned char *key, size_t keylen, + unsigned char *iv, size_t ivlen, + const unsigned char *salt, size_t saltlen, + const unsigned char *secret, size_t secretlen) +{ + const unsigned char key_label[] = "token key"; + const unsigned char iv_label[] = "token iv"; + + return quic_do_tls_derive_retry_token_secret(md, key, keylen, iv, ivlen, + salt, saltlen, secret, secretlen, + key_label, sizeof(key_label) - 1, + iv_label, sizeof(iv_label) -1); +} + /* Generate the AEAD tag for the Retry packet of bytes and * write it to . The tag is written just after the area. It should * be at least 16 bytes longs. is the CID of the Initial packet diff --git a/src/quic_token.c b/src/quic_token.c new file mode 100644 index 000000000..85fe5f712 --- /dev/null +++ b/src/quic_token.c @@ -0,0 +1,193 @@ +#include +#include + +#define TRACE_SOURCE &trace_quic + +#define QUIC_TOKEN_RAND_DLEN 16 + +/* Copy only the IP address from 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_ip_addr_cpy(unsigned char *buf, + const struct sockaddr_storage *saddr) +{ + void *addr; + unsigned char *p; + size_t addr_len; + + p = buf; + if (saddr->ss_family == AF_INET6) { + addr = &((struct sockaddr_in6 *)saddr)->sin6_addr; + addr_len = sizeof ((struct sockaddr_in6 *)saddr)->sin6_addr; + } + else { + addr = &((struct sockaddr_in *)saddr)->sin_addr; + addr_len = sizeof ((struct sockaddr_in *)saddr)->sin_addr; + } + memcpy(p, addr, addr_len); + p += addr_len; + + return p - buf; +} + +int quic_generate_token(unsigned char *token, size_t len, + struct sockaddr_storage *addr) +{ +#ifdef QUIC_AEAD_API + const QUIC_AEAD *aead = EVP_aead_aes_128_gcm(); +#else + const QUIC_AEAD *aead = EVP_aes_128_gcm(); +#endif + int ret = 0; + unsigned char *p; + unsigned char aad[sizeof(struct in6_addr)]; + size_t aadlen; + uint32_t ts = (uint32_t)date.tv_sec; + uint64_t rand_u64; + unsigned char rand[QUIC_TOKEN_RAND_DLEN]; + unsigned char key[16]; + unsigned char iv[QUIC_TLS_IV_LEN]; + const unsigned char *sec = global.cluster_secret; + size_t seclen = sizeof(global.cluster_secret); + QUIC_AEAD_CTX *ctx = NULL; + + TRACE_ENTER(QUIC_EV_CONN_TXPKT); + + /* Generate random data to be used as salt to derive the token secret. */ + rand_u64 = ha_random64(); + write_u64(rand, rand_u64); + rand_u64 = ha_random64(); + write_u64(rand + sizeof(rand_u64), rand_u64); + + if (len < QUIC_TOKEN_LEN) { + TRACE_ERROR("too small buffer", QUIC_EV_CONN_TXPKT); + goto err; + } + + /* Generate the AAD. */ + aadlen = quic_ip_addr_cpy(aad, addr); + if (!quic_tls_derive_token_secret(EVP_sha256(), key, sizeof key, + iv, sizeof iv, rand, sizeof(rand), + sec, seclen)) { + TRACE_ERROR("quic_tls_derive_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; + } + + /* Clear token build */ + p = token; + *p++ = QUIC_TOKEN_FMT_NEW; + write_u32(p, htonl(ts)); + p += sizeof(ts); + + 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, rand, sizeof(rand)); + p += sizeof(rand); + QUIC_AEAD_CTX_free(ctx); + + ret = p - token; + leave: + TRACE_LEAVE(QUIC_EV_CONN_TXPKT); + return ret; + + err: + TRACE_DEVEL("leaving on error", QUIC_EV_CONN_TXPKT); + if (ctx) + QUIC_AEAD_CTX_free(ctx); + goto leave; +} + +/* QUIC server only function. + * + * Check the validity of the token from Initial packet . is + * the UDP datagram containing and is the listener instance on which + * it was received. is used only for debugging purposes (traces). + * + * Return 1 if succeeded, 0 if not. + */ +int quic_token_check(struct quic_rx_packet *pkt, + struct quic_dgram *dgram, + struct quic_conn *qc) +{ + int ret = 0; + unsigned char *token = pkt->token; + size_t tokenlen = pkt->token_len; + const unsigned char *rand; + unsigned char buf[128]; + unsigned char aad[sizeof(in_port_t) + sizeof(struct in6_addr)]; + size_t aadlen; + unsigned char key[16]; + unsigned char iv[QUIC_TLS_IV_LEN]; + const unsigned char *sec = global.cluster_secret; + size_t seclen = sizeof(global.cluster_secret); + uint32_t ts; + uint32_t now_sec = (uint32_t)date.tv_sec; + + QUIC_AEAD_CTX *ctx = NULL; + +#ifdef QUIC_AEAD_API + const QUIC_AEAD *aead = EVP_aead_aes_128_gcm(); +#else + const QUIC_AEAD *aead = EVP_aes_128_gcm(); +#endif + + TRACE_ENTER(QUIC_EV_CONN_LPKT, qc); + + BUG_ON(!pkt->token_len || *pkt->token != QUIC_TOKEN_FMT_NEW); + + if (sizeof(buf) < tokenlen) { + TRACE_ERROR("too short buffer", QUIC_EV_CONN_LPKT, qc); + goto err; + } + + /* Generate the AAD. */ + aadlen = quic_ip_addr_cpy(aad, &dgram->saddr); + rand = token + tokenlen - QUIC_TOKEN_RAND_DLEN; + if (!quic_tls_derive_token_secret(EVP_sha256(), key, sizeof key, iv, sizeof iv, + rand, QUIC_TOKEN_RAND_DLEN, sec, seclen)) { + TRACE_ERROR("Could not derive token 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_TOKEN_RAND_DLEN - 1, aad, aadlen, + ctx, aead, key, iv)) { + TRACE_ERROR("Could not decrypt token", QUIC_EV_CONN_LPKT, qc); + goto err; + } + + ts = ntohl(read_u32(buf)); + if (now_sec - ts > QUIC_TOKEN_DURATION_SEC) { + TRACE_ERROR("expired token", QUIC_EV_CONN_LPKT, qc); + goto err; + } + + ret = 1; + QUIC_AEAD_CTX_free(ctx); + leave: + TRACE_LEAVE(QUIC_EV_CONN_LPKT); + return ret; + + err: + TRACE_DEVEL("leaving on error", QUIC_EV_CONN_LPKT); + if (ctx) + QUIC_AEAD_CTX_free(ctx); + goto leave; +} + diff --git a/src/quic_trace.c b/src/quic_trace.c index ce683f657..26ebb6eef 100644 --- a/src/quic_trace.c +++ b/src/quic_trace.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -260,9 +261,13 @@ static void quic_trace(enum trace_level level, uint64_t mask, const struct trace if (mask & QUIC_EV_CONN_IO_CB) { const enum quic_handshake_state *state = a2; + const SSL *ssl = a3; if (state) chunk_appendf(&trace_buf, " state=%s", quic_hdshk_state_str(*state)); + if (ssl) + chunk_appendf(&trace_buf, " early_data_status=%s", + quic_ssl_early_data_status_str(ssl)); } if (mask & (QUIC_EV_CONN_TRMHP|QUIC_EV_CONN_ELRMHP|QUIC_EV_CONN_SPKT)) {