From 7a8b2ece3a61b98e37647280f0e2d00a142e0db8 Mon Sep 17 00:00:00 2001 From: Dmitri Tikhonov Date: Mon, 23 Dec 2019 16:14:20 -0500 Subject: [PATCH] Release 2.8.0 - [FEATURE] Add support for Q050. - [OPTIMIZATION] Reduce mallocs in gQUIC handshake. - [BUGFIX] Disable redo of failed STREAM frame insertion with debug logging. --- CHANGELOG | 7 + README.md | 2 +- include/lsquic.h | 15 +- src/liblsquic/CMakeLists.txt | 2 +- src/liblsquic/lsquic_buf.c | 92 --- src/liblsquic/lsquic_buf.h | 35 - src/liblsquic/lsquic_crypto.c | 90 +-- src/liblsquic/lsquic_crypto.h | 8 +- src/liblsquic/lsquic_enc_sess.h | 10 +- src/liblsquic/lsquic_engine.c | 2 + src/liblsquic/lsquic_full_conn.c | 246 ++++-- src/liblsquic/lsquic_handshake.c | 848 ++++++++++++++++++--- src/liblsquic/lsquic_hspack_valid.c | 3 +- src/liblsquic/lsquic_mini_conn.c | 359 +++++++-- src/liblsquic/lsquic_mini_conn.h | 4 +- src/liblsquic/lsquic_mini_conn_ietf.c | 2 +- src/liblsquic/lsquic_packet_common.h | 2 +- src/liblsquic/lsquic_packet_out.c | 12 +- src/liblsquic/lsquic_packet_out.h | 2 +- src/liblsquic/lsquic_parse.h | 17 +- src/liblsquic/lsquic_parse_Q046.c | 34 +- src/liblsquic/lsquic_parse_Q050.c | 866 ++++++++++++++++++++++ src/liblsquic/lsquic_parse_common.c | 64 +- src/liblsquic/lsquic_parse_common.h | 4 +- src/liblsquic/lsquic_parse_gquic_be.c | 34 +- src/liblsquic/lsquic_parse_gquic_common.c | 8 +- src/liblsquic/lsquic_parse_ietf_v1.c | 76 +- src/liblsquic/lsquic_stream.c | 64 +- src/liblsquic/lsquic_stream.h | 17 +- src/liblsquic/lsquic_version.c | 2 + test/unittests/CMakeLists.txt | 1 - test/unittests/test_buf.c | 44 -- test/unittests/test_export_key.c | 45 +- test/unittests/test_hkdf.c | 6 +- test/unittests/test_spi.c | 8 +- test/unittests/test_stream.c | 19 +- 36 files changed, 2477 insertions(+), 573 deletions(-) delete mode 100644 src/liblsquic/lsquic_buf.c delete mode 100644 src/liblsquic/lsquic_buf.h create mode 100644 src/liblsquic/lsquic_parse_Q050.c delete mode 100644 test/unittests/test_buf.c diff --git a/CHANGELOG b/CHANGELOG index 032ac16..c522320 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +2019-12-23 + - 2.8.0 + - [FEATURE] Add support for Q050. + - [OPTIMIZATION] Reduce mallocs in gQUIC handshake. + - [BUGFIX] Disable redo of failed STREAM frame insertion with debug + logging. + 2019-12-18 - 2.7.3 - [DEBUG] Further dedup next advisory tick messages when reason is diff --git a/README.md b/README.md index 1ee0b19..47d9134 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ and OpenLiteSpeed. We think it is free of major problems. Nevertheless, do not hesitate to report bugs back to us. Even better, send us fixes and improvements! -Currently supported QUIC versions are Q039, Q043, Q046, ID-23, and ID-24. +Currently supported QUIC versions are Q039, Q043, Q046, Q050, ID-23, and ID-24. Support for newer versions will be added soon after they are released. Documentation diff --git a/include/lsquic.h b/include/lsquic.h index 5f5d33e..9ee2df6 100644 --- a/include/lsquic.h +++ b/include/lsquic.h @@ -24,8 +24,8 @@ extern "C" { #endif #define LSQUIC_MAJOR_VERSION 2 -#define LSQUIC_MINOR_VERSION 7 -#define LSQUIC_PATCH_VERSION 3 +#define LSQUIC_MINOR_VERSION 8 +#define LSQUIC_PATCH_VERSION 0 /** * Engine flags: @@ -112,6 +112,13 @@ enum lsquic_version */ LSQVER_046, + /** + * Q050. Variable-length QUIC server connection IDs. Use CRYPTO frames + * for handshake. IETF header format matching invariants-06. Packet + * number encryption. Initial packets are obfuscated. + */ + LSQVER_050, + #if LSQUIC_USE_Q098 /** * Q098. This is a made-up, experimental version used to test version @@ -144,7 +151,7 @@ enum lsquic_version }; /** - * We currently support versions 39, 43, 46, and IETF Draft-23 + * We currently support versions 39, 43, 46, 50, and IETF Draft-23 and Draft-24 * @see lsquic_version */ #define LSQUIC_SUPPORTED_VERSIONS ((1 << N_LSQVER) - 1) @@ -152,7 +159,7 @@ enum lsquic_version /** * List of versions in which the server never includes CID in short packets. */ -#define LSQUIC_FORCED_TCID0_VERSIONS (1 << LSQVER_046) +#define LSQUIC_FORCED_TCID0_VERSIONS ((1 << LSQVER_046)|(1 << LSQVER_050)) #define LSQUIC_EXPERIMENTAL_VERSIONS ( \ (1 << LSQVER_VERNEG) | LSQUIC_EXPERIMENTAL_Q098) diff --git a/src/liblsquic/CMakeLists.txt b/src/liblsquic/CMakeLists.txt index 766141a..e03a906 100644 --- a/src/liblsquic/CMakeLists.txt +++ b/src/liblsquic/CMakeLists.txt @@ -5,7 +5,6 @@ SET(lsquic_STAT_SRCS lsquic_arr.c lsquic_attq.c lsquic_bbr.c - lsquic_buf.c lsquic_bw_sampler.c lsquic_cfcw.c lsquic_chsk_stream.c @@ -50,6 +49,7 @@ SET(lsquic_STAT_SRCS lsquic_packet_out.c lsquic_packints.c lsquic_parse_Q046.c + lsquic_parse_Q050.c lsquic_parse_common.c lsquic_parse_gquic_be.c lsquic_parse_gquic_common.c diff --git a/src/liblsquic/lsquic_buf.c b/src/liblsquic/lsquic_buf.c deleted file mode 100644 index ef01d7d..0000000 --- a/src/liblsquic/lsquic_buf.c +++ /dev/null @@ -1,92 +0,0 @@ -/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */ -/* - * lsquic_buf.c - */ - -#include -#include -#include - -#ifdef WIN32 -#include -#endif -#include "lsquic_buf.h" - - -static int -lsquic_buf_reserve (struct lsquic_buf *buf, int size) -{ - char *new_buf; - - if (buf->bufend - buf->buf == size) - return 0; - - new_buf = realloc(buf->buf, size); - if (new_buf != 0 || size == 0) - { - buf->end = new_buf + (buf->end - buf->buf); - buf->buf = new_buf; - buf->bufend = new_buf + size; - if (buf->end > buf->bufend) - buf->end = buf->bufend; - return 0; - } - else - return -1; -} - - -static int -lsquic_buf_grow (struct lsquic_buf *buf, int size) -{ - size = ((size + 511) >> 9) << 9; - return lsquic_buf_reserve(buf, lsquic_buf_capacity(buf) + size); -} - - -struct lsquic_buf * -lsquic_buf_create (int size) -{ - struct lsquic_buf *buf; - - buf = calloc(1, sizeof(*buf)); - if (!buf) - return NULL; - - if (0 != lsquic_buf_reserve(buf, size)) - { - free(buf); - return NULL; - } - - return buf; -} - - -int -lsquic_buf_append (struct lsquic_buf *buf, const char *str, int size) -{ - if (buf == NULL || size < 0) - { - errno = EINVAL; - return -1; - } - if (size == 0) - return 0; - if (size > lsquic_buf_avail(buf)) - { - if (lsquic_buf_grow(buf, size - lsquic_buf_avail(buf)) != 0) - return -1; - } - memmove(buf->end, str, size); - buf->end += size; - return size; -} - - -void -lsquic_buf_destroy (struct lsquic_buf *buf) -{ - free(buf->buf); - free(buf); -} diff --git a/src/liblsquic/lsquic_buf.h b/src/liblsquic/lsquic_buf.h deleted file mode 100644 index 55b983c..0000000 --- a/src/liblsquic/lsquic_buf.h +++ /dev/null @@ -1,35 +0,0 @@ -/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */ -/* - * lsquic_buf.h - */ - -#ifndef LSQUIC_BUF_H -#define LSQUIC_BUF_H 1 - -struct lsquic_buf -{ - char *buf, *end, *bufend; -}; - -struct lsquic_buf * -lsquic_buf_create (int); - -int -lsquic_buf_append (struct lsquic_buf *, const char *, int); - -#define lsquic_buf_begin(buf_) ((buf_)->buf) - -#define lsquic_buf_size(buf_) ((buf_)->end - (buf_)->buf) - -#define lsquic_buf_avail(buf_) ((buf_)->bufend - (buf_)->end) - -#define lsquic_buf_capacity(buf_) ((buf_)->bufend - (buf_)->buf) - -#define lsquic_buf_clear(buf_) do { \ - (buf_)->end = (buf_)->buf; \ -} while (0) - -void -lsquic_buf_destroy (struct lsquic_buf *); - -#endif diff --git a/src/liblsquic/lsquic_crypto.c b/src/liblsquic/lsquic_crypto.c index 9141711..494c9fa 100644 --- a/src/liblsquic/lsquic_crypto.c +++ b/src/liblsquic/lsquic_crypto.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -208,58 +209,30 @@ int lshkdf_expand(const unsigned char *prk, const unsigned char *info, int info_ uint16_t s_key_len, uint8_t *s_key, uint16_t c_key_iv_len, uint8_t *c_key_iv, uint16_t s_key_iv_len, uint8_t *s_key_iv, - uint16_t sub_key_len, uint8_t *sub_key) + uint16_t sub_key_len, uint8_t *sub_key, + uint8_t *c_hp, uint8_t *s_hp) { - int L = c_key_len + s_key_len + c_key_iv_len + s_key_iv_len + sub_key_len; - int N = (L + SHA256LEN - 1) / SHA256LEN; - unsigned char *p_org; - uint8_t *buf; + const unsigned L = c_key_len + s_key_len + c_key_iv_len + s_key_iv_len + + sub_key_len + + (c_hp ? c_key_len : 0) + + (s_hp ? s_key_len : 0) + ; unsigned char *p; - unsigned char T[SHA256LEN + 1]; - int T_len = 0; - int i; - uint8_t *pb; + unsigned char output[ + EVP_MAX_KEY_LENGTH * 2 /* Keys */ + + EVP_MAX_IV_LENGTH * 2 /* IVs */ + + 32 /* Subkey */ + + EVP_MAX_KEY_LENGTH * 2 /* Header protection */ + ]; - p_org = malloc(N * SHA256LEN); - if (!p_org) - return -1; + assert((size_t) L <= sizeof(output)); - buf = malloc(SHA256LEN + info_len + 13); - if (!buf) - { - free(p_org); - return -1; - } - - p = p_org; - - for (i = 1; i <= N; ++i) - { - pb = buf; - if (T_len > 0) - { - memcpy(pb, T, T_len); - pb += T_len; - } - - memcpy(pb, info, info_len); - pb += info_len; - *pb = i; - ++pb; - - HMAC(EVP_sha256(), prk, SHA256LEN, buf, pb - buf, T, NULL); - if (i != N) - T_len = SHA256LEN; - else - T_len = L - (N - 1) * SHA256LEN; - - memcpy(p, T, T_len); - p += T_len; - } - - free(buf); - - p = p_org; +#ifndef NDEBUG + const int s = +#endif + HKDF_expand(output, L, EVP_sha256(), prk, 32, info, info_len); + assert(s); + p = output; if (c_key_len) { memcpy(c_key, p, c_key_len); @@ -285,8 +258,16 @@ int lshkdf_expand(const unsigned char *prk, const unsigned char *info, int info_ memcpy(sub_key, p, sub_key_len); p += sub_key_len; } - - free(p_org); + if (c_key_len && c_hp) + { + memcpy(c_hp, p, c_key_len); + p += c_key_len; + } + if (s_key_len && s_hp) + { + memcpy(s_hp, p, s_key_len); + p += s_key_len; + } return 0; } @@ -313,20 +294,21 @@ int export_key_material_simple(unsigned char *ikm, uint32_t ikm_len, memcpy(info + info_len, context, context_len); info_len += context_len; lshkdf_expand(prk, info, info_len, key_len, key, - 0, NULL, 0, NULL,0, NULL, 0, NULL); + 0, NULL, 0, NULL,0, NULL, 0, NULL, NULL, NULL); free(info); return 0; } -int export_key_material(const unsigned char *ikm, uint32_t ikm_len, +int +lsquic_export_key_material(const unsigned char *ikm, uint32_t ikm_len, const unsigned char *salt, int salt_len, const unsigned char *context, uint32_t context_len, uint16_t c_key_len, uint8_t *c_key, uint16_t s_key_len, uint8_t *s_key, uint16_t c_key_iv_len, uint8_t *c_key_iv, uint16_t s_key_iv_len, uint8_t *s_key_iv, - uint8_t *sub_key) + uint8_t *sub_key, uint8_t *c_hp, uint8_t *s_hp) { unsigned char prk[32]; uint16_t sub_key_len = ikm_len; @@ -334,7 +316,7 @@ int export_key_material(const unsigned char *ikm, uint32_t ikm_len, lshkdf_extract(ikm, ikm_len, salt, salt_len, prk); lshkdf_expand(prk, context, context_len, c_key_len, c_key, s_key_len, s_key, c_key_iv_len, c_key_iv, s_key_iv_len, - s_key_iv, sub_key_len, sub_key); + s_key_iv, sub_key_len, sub_key, c_hp, s_hp); return 0; } diff --git a/src/liblsquic/lsquic_crypto.h b/src/liblsquic/lsquic_crypto.h index f9d5a4f..03f009c 100644 --- a/src/liblsquic/lsquic_crypto.h +++ b/src/liblsquic/lsquic_crypto.h @@ -40,14 +40,15 @@ int export_key_material_simple(unsigned char *ikm, uint32_t ikm_len, const uint8_t *context, uint32_t context_len, uint8_t *key, uint16_t key_len); -int export_key_material(const unsigned char *ikm, uint32_t ikm_len, +int lsquic_export_key_material(const unsigned char *ikm, uint32_t ikm_len, const unsigned char *salt, int salt_len, const unsigned char *context, uint32_t context_len, uint16_t c_key_len, uint8_t *c_key, uint16_t s_key_len, uint8_t *s_key, uint16_t c_key_iv_len, uint8_t *c_key_iv, uint16_t s_key_iv_len, uint8_t *s_key_iv, - uint8_t *sub_key); + uint8_t *sub_key, + uint8_t *c_hp, uint8_t *s_hp); void c255_get_pub_key(unsigned char *priv_key, unsigned char pub_key[32]); int c255_gen_share_key(unsigned char *priv_key, unsigned char *peer_pub_key, unsigned char *shared_key); @@ -88,7 +89,8 @@ int lshkdf_expand(const unsigned char *prk, const unsigned char *info, int info_ uint16_t s_key_len, uint8_t *s_key, uint16_t c_key_iv_len, uint8_t *c_key_iv, uint16_t s_key_iv_len, uint8_t *s_key_iv, - uint16_t sub_key_len, uint8_t *sub_key); + uint16_t sub_key_len, uint8_t *sub_key, + uint8_t *c_hp, uint8_t *s_hp); void lshkdf_extract(const unsigned char *ikm, int ikm_len, const unsigned char *salt, int salt_len, unsigned char *prk); diff --git a/src/liblsquic/lsquic_enc_sess.h b/src/liblsquic/lsquic_enc_sess.h index 052ef8d..0299ca6 100644 --- a/src/liblsquic/lsquic_enc_sess.h +++ b/src/liblsquic/lsquic_enc_sess.h @@ -169,7 +169,7 @@ struct enc_session_funcs_gquic #ifndef NDEBUG /* Need to expose this function for testing */ int (*esf_determine_diversification_key) (enc_session_t *, - uint8_t *diversification_nonce, int is_client); + uint8_t *diversification_nonce); #endif const char * @@ -310,6 +310,13 @@ extern const #endif struct enc_session_funcs_common lsquic_enc_session_common_gquic_1; + +extern +#ifdef NDEBUG +const +#endif +struct enc_session_funcs_common lsquic_enc_session_common_gquic_2; + extern const struct enc_session_funcs_common lsquic_enc_session_common_ietf_v1; extern @@ -324,6 +331,7 @@ extern const struct enc_session_funcs_iquic lsquic_enc_session_iquic_ietf_v1; ver == LSQVER_ID23 ? &lsquic_enc_session_common_ietf_v1 : \ ver == LSQVER_ID24 ? &lsquic_enc_session_common_ietf_v1 : \ ver == LSQVER_VERNEG ? &lsquic_enc_session_common_ietf_v1 : \ + ver == LSQVER_050 ? &lsquic_enc_session_common_gquic_2 : \ &lsquic_enc_session_common_gquic_1 ) #define select_esf_gquic_by_ver(ver) ( \ diff --git a/src/liblsquic/lsquic_engine.c b/src/liblsquic/lsquic_engine.c index 6ddd9bb..833d421 100644 --- a/src/liblsquic/lsquic_engine.c +++ b/src/liblsquic/lsquic_engine.c @@ -2616,6 +2616,8 @@ lsquic_engine_packet_in (lsquic_engine_t *engine, parse_packet_in_begin = lsquic_gquic_parse_packet_in_begin; else if ((1 << conn->cn_version) & LSQUIC_IETF_VERSIONS) parse_packet_in_begin = lsquic_ietf_v1_parse_packet_in_begin; + else if (conn->cn_version == LSQVER_050) + parse_packet_in_begin = lsquic_Q050_parse_packet_in_begin; else { assert(conn->cn_version == LSQVER_046 diff --git a/src/liblsquic/lsquic_full_conn.c b/src/liblsquic/lsquic_full_conn.c index 334ab1f..027c569 100644 --- a/src/liblsquic/lsquic_full_conn.c +++ b/src/liblsquic/lsquic_full_conn.c @@ -75,7 +75,7 @@ #define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(&conn->fc_conn) #include "lsquic_logger.h" -enum { STREAM_IF_STD, STREAM_IF_HSK, STREAM_IF_HDR, N_STREAM_IFS }; +enum stream_if { STREAM_IF_STD, STREAM_IF_HSK, STREAM_IF_HDR, N_STREAM_IFS }; #define MAX_RETR_PACKETS_SINCE_LAST_ACK 2 #define ACK_TIMEOUT 25000 @@ -220,6 +220,7 @@ struct full_conn lsquic_time_t fc_saved_ack_received; struct network_path fc_path; unsigned fc_orig_versions; /* Client only */ + enum enc_level fc_crypto_enc_level; }; static const struct ver_neg server_ver_neg; @@ -262,6 +263,10 @@ static lsquic_stream_t * new_stream (struct full_conn *conn, lsquic_stream_id_t stream_id, enum stream_ctor_flags); +static struct lsquic_stream * +new_stream_ext (struct full_conn *, lsquic_stream_id_t, enum stream_if, + enum stream_ctor_flags); + static void reset_ack_state (struct full_conn *conn); @@ -550,9 +555,59 @@ apply_peer_settings (struct full_conn *conn) static const struct conn_iface *full_conn_iface_ptr; + +/* gQUIC up to version Q046 has handshake stream 1 and headers stream 3. + * Q050 and later have "crypto streams" -- meaning CRYPTO frames, not + * STREAM frames and no stream IDs -- and headers stream 1. + */ +static lsquic_stream_id_t +headers_stream_id_by_ver (enum lsquic_version version) +{ + if (version < LSQVER_050) + return 3; + else + return 1; +} + + +static lsquic_stream_id_t +headers_stream_id_by_conn (const struct full_conn *conn) +{ + return headers_stream_id_by_ver(conn->fc_conn.cn_version); +} + + +static lsquic_stream_id_t +hsk_stream_id (const struct full_conn *conn) +{ + if (conn->fc_conn.cn_version < LSQVER_050) + return 1; + else + /* Use this otherwise invalid stream ID as ID for the gQUIC crypto + * stream. + */ + return (uint64_t) -1; +} + + +static int +has_handshake_stream (const struct full_conn *conn) +{ + return conn->fc_conn.cn_version < LSQVER_050; +} + + +static int +is_handshake_stream_id (const struct full_conn *conn, + lsquic_stream_id_t stream_id) +{ + return conn->fc_conn.cn_version < LSQVER_050 && stream_id == 1; +} + + static struct full_conn * new_conn_common (lsquic_cid_t cid, struct lsquic_engine_public *enpub, - unsigned flags) + unsigned flags, enum lsquic_version version) { struct full_conn *conn; lsquic_stream_t *headers_stream; @@ -635,8 +690,9 @@ new_conn_common (lsquic_cid_t cid, struct lsquic_engine_public *enpub, goto cleanup_on_error; conn->fc_stream_ifs[STREAM_IF_HDR].stream_if = lsquic_headers_stream_if; conn->fc_stream_ifs[STREAM_IF_HDR].stream_if_ctx = conn->fc_pub.u.gquic.hs; - headers_stream = new_stream(conn, LSQUIC_GQUIC_STREAM_HEADERS, - SCF_CALL_ON_NEW); + headers_stream = new_stream_ext(conn, headers_stream_id_by_ver(version), + STREAM_IF_HDR, + SCF_CALL_ON_NEW|SCF_DI_AUTOSWITCH|SCF_CRITICAL|SCF_HEADERS); if (!headers_stream) goto cleanup_on_error; } @@ -701,9 +757,10 @@ lsquic_gquic_full_conn_client_new (struct lsquic_engine_public *enpub, else max_packet_size = GQUIC_MAX_IPv6_PACKET_SZ; } - conn = new_conn_common(cid, enpub, flags); + conn = new_conn_common(cid, enpub, flags, version); if (!conn) return NULL; + init_ver_neg(conn, versions, &version); conn->fc_path.np_pack_size = max_packet_size; conn->fc_conn.cn_esf_c = select_esf_common_by_ver(version); conn->fc_conn.cn_esf.g = esf_g; @@ -718,9 +775,11 @@ lsquic_gquic_full_conn_client_new (struct lsquic_engine_public *enpub, } if (conn->fc_flags & FC_HTTP) - conn->fc_last_stream_id = LSQUIC_GQUIC_STREAM_HEADERS; /* Client goes 5, 7, 9.... */ + conn->fc_last_stream_id = headers_stream_id_by_conn(conn); /* Client goes (3?), 5, 7, 9.... */ + else if (has_handshake_stream(conn)) + conn->fc_last_stream_id = 1; else - conn->fc_last_stream_id = LSQUIC_GQUIC_STREAM_HANDSHAKE; + conn->fc_last_stream_id = (uint64_t) -1; /* +2 will get us to 1 */ conn->fc_hsk_ctx.client.lconn = &conn->fc_conn; conn->fc_hsk_ctx.client.mm = &enpub->enp_mm; conn->fc_hsk_ctx.client.ver_neg = &conn->fc_ver_neg; @@ -728,11 +787,12 @@ lsquic_gquic_full_conn_client_new (struct lsquic_engine_public *enpub, .stream_if = &lsquic_client_hsk_stream_if; conn->fc_stream_ifs[STREAM_IF_HSK].stream_if_ctx = &conn->fc_hsk_ctx.client; conn->fc_orig_versions = versions; - init_ver_neg(conn, versions, &version); if (conn->fc_settings->es_handshake_to) lsquic_alarmset_set(&conn->fc_alset, AL_HANDSHAKE, lsquic_time_now() + conn->fc_settings->es_handshake_to); - if (!new_stream(conn, LSQUIC_GQUIC_STREAM_HANDSHAKE, SCF_CALL_ON_NEW)) + if (!new_stream_ext(conn, hsk_stream_id(conn), STREAM_IF_HSK, + SCF_CALL_ON_NEW|SCF_DI_AUTOSWITCH|SCF_CRITICAL|SCF_CRYPTO + |(conn->fc_conn.cn_version >= LSQVER_050 ? SCF_CRYPTO_FRAMES : 0))) { LSQ_WARN("could not create handshake stream: %s", strerror(errno)); conn->fc_conn.cn_if->ci_destroy(&conn->fc_conn); @@ -776,15 +836,18 @@ lsquic_gquic_full_conn_server_new (struct lsquic_engine_public *enpub, int have_outgoing_ack = 0; mc = (struct mini_conn *) lconn_mini; - conn = new_conn_common(lconn_mini->cn_cid, enpub, flags); + conn = new_conn_common(lconn_mini->cn_cid, enpub, flags, + lconn_mini->cn_version); if (!conn) return NULL; lconn_full = &conn->fc_conn; conn->fc_last_stream_id = 0; /* Server goes 2, 4, 6.... */ if (conn->fc_flags & FC_HTTP) - conn->fc_max_peer_stream_id = LSQUIC_GQUIC_STREAM_HEADERS; + conn->fc_max_peer_stream_id = headers_stream_id_by_conn(conn); + else if (has_handshake_stream(conn)) + conn->fc_max_peer_stream_id = 1; else - conn->fc_max_peer_stream_id = LSQUIC_GQUIC_STREAM_HANDSHAKE; + conn->fc_max_peer_stream_id = (uint64_t) -1; conn->fc_stream_ifs[STREAM_IF_HSK] .stream_if = &lsquic_server_hsk_stream_if; conn->fc_stream_ifs[STREAM_IF_HSK].stream_if_ctx = &conn->fc_hsk_ctx.server; @@ -799,8 +862,15 @@ lsquic_gquic_full_conn_server_new (struct lsquic_engine_public *enpub, conn->fc_hsk_ctx.server.lconn = lconn_full; conn->fc_hsk_ctx.server.enpub = enpub; + /* TODO Optimize: we don't need an actual crypto stream and handler + * on the server side, as we don't do anything with it. We can + * throw out appropriate frames earlier. + */ + /* Adjust offsets in the HANDSHAKE stream: */ - hsk_stream = new_stream(conn, LSQUIC_GQUIC_STREAM_HANDSHAKE, SCF_CALL_ON_NEW); + hsk_stream = new_stream_ext(conn, hsk_stream_id(conn), STREAM_IF_HSK, + SCF_CALL_ON_NEW|SCF_DI_AUTOSWITCH|SCF_CRITICAL|SCF_CRYPTO + |(conn->fc_conn.cn_version >= LSQVER_050 ? SCF_CRYPTO_FRAMES : 0)); if (!hsk_stream) { LSQ_DEBUG("could not create handshake stream: %s", strerror(errno)); @@ -1236,15 +1306,15 @@ full_conn_ci_write_ack (struct lsquic_conn *lconn, static lsquic_stream_t * -new_stream_ext (struct full_conn *conn, lsquic_stream_id_t stream_id, int if_idx, - enum stream_ctor_flags stream_ctor_flags) +new_stream_ext (struct full_conn *conn, lsquic_stream_id_t stream_id, + enum stream_if if_idx, enum stream_ctor_flags stream_ctor_flags) { struct lsquic_stream *stream; stream = lsquic_stream_new(stream_id, &conn->fc_pub, conn->fc_stream_ifs[if_idx].stream_if, conn->fc_stream_ifs[if_idx].stream_if_ctx, conn->fc_settings->es_sfcw, - stream_id == LSQUIC_GQUIC_STREAM_HANDSHAKE + stream_ctor_flags & SCF_CRYPTO ? 16 * 1024 : conn->fc_cfg.max_stream_send, stream_ctor_flags); if (stream) @@ -1258,30 +1328,13 @@ static lsquic_stream_t * new_stream (struct full_conn *conn, lsquic_stream_id_t stream_id, enum stream_ctor_flags flags) { - int idx; - switch (stream_id) - { - case LSQUIC_GQUIC_STREAM_HANDSHAKE: - idx = STREAM_IF_HSK; - flags |= SCF_DI_AUTOSWITCH|SCF_CRITICAL; - break; - case LSQUIC_GQUIC_STREAM_HEADERS: - idx = STREAM_IF_HDR; - flags |= SCF_DI_AUTOSWITCH|SCF_CRITICAL; - if (!(conn->fc_flags & FC_HTTP) && - conn->fc_enpub->enp_settings.es_rw_once) - flags |= SCF_DISP_RW_ONCE; - break; - default: - idx = STREAM_IF_STD; - flags |= SCF_DI_AUTOSWITCH; - if (conn->fc_pub.u.gquic.hs) - flags |= SCF_HTTP; - if (conn->fc_enpub->enp_settings.es_rw_once) - flags |= SCF_DISP_RW_ONCE; - break; - } - return new_stream_ext(conn, stream_id, idx, flags); + flags |= SCF_DI_AUTOSWITCH; + if (conn->fc_pub.u.gquic.hs) + flags |= SCF_HTTP; + if (conn->fc_enpub->enp_settings.es_rw_once) + flags |= SCF_DISP_RW_ONCE; + + return new_stream_ext(conn, stream_id, STREAM_IF_STD, flags); } @@ -1492,7 +1545,7 @@ process_stream_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in, int parsed_len; #ifndef LSQUIC_REDO_FAILED_INSERTION -#define LSQUIC_REDO_FAILED_INSERTION 1 +#define LSQUIC_REDO_FAILED_INSERTION 0 #endif #if LSQUIC_REDO_FAILED_INSERTION enum lsq_log_level saved_levels[3]; @@ -1526,12 +1579,11 @@ process_stream_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in, #endif enc_level = lsquic_packet_in_enc_level(packet_in); - if (stream_frame->stream_id != LSQUIC_GQUIC_STREAM_HANDSHAKE + if (!is_handshake_stream_id(conn, stream_frame->stream_id) #if LSQUIC_ENABLE_HANDSHAKE_DISABLE && !(conn->fc_conn.cn_flags & LSCONN_NO_CRYPTO) #endif - && enc_level != ENC_LEV_FORW - && enc_level != ENC_LEV_INIT) + && enc_level == ENC_LEV_CLEAR) { lsquic_malo_put(stream_frame); ABORT_ERROR("received unencrypted data for stream %"PRIu64, @@ -1641,7 +1693,7 @@ process_stream_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in, return 0; } - if (stream->id == LSQUIC_GQUIC_STREAM_HANDSHAKE + if (lsquic_stream_is_crypto(stream) && (stream->sm_qflags & SMQF_WANT_READ) && !(conn->fc_flags & FC_SERVER) && !(conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)) @@ -1660,6 +1712,83 @@ process_stream_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in, } +static unsigned +process_crypto_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in, + const unsigned char *p, size_t len) +{ + struct lsquic_stream *stream; + stream_frame_t *stream_frame; + enum enc_level enc_level; + int parsed_len; + + stream_frame = lsquic_malo_get(conn->fc_pub.mm->malo.stream_frame); + if (!stream_frame) + { + LSQ_WARN("could not allocate stream frame: %s", strerror(errno)); + return 0; + } + + parsed_len = conn->fc_conn.cn_pf->pf_parse_crypto_frame(p, len, + stream_frame); + if (parsed_len < 0) + { + lsquic_malo_put(stream_frame); + return 0; + } + enc_level = lsquic_packet_in_enc_level(packet_in); + EV_LOG_CRYPTO_FRAME_IN(LSQUIC_LOG_CONN_ID, stream_frame, enc_level); + LSQ_DEBUG("Got CRYPTO frame on enc level %s", lsquic_enclev2str[enc_level]); + + if (enc_level < conn->fc_crypto_enc_level) + { + LSQ_DEBUG("Old enc level: ignore frame"); + lsquic_malo_put(stream_frame); + return parsed_len; + } + + if (conn->fc_flags & FC_CLOSING) + { + LSQ_DEBUG("Connection closing: ignore frame"); + lsquic_malo_put(stream_frame); + return parsed_len; + } + + stream = find_stream_by_id(conn, hsk_stream_id(conn)); + if (!stream) + { + LSQ_WARN("cannot find handshake stream for CRYPTO frame"); + lsquic_malo_put(stream_frame); + return 0; + } + + if (enc_level > conn->fc_crypto_enc_level) + { + stream->read_offset = 0; + stream->tosend_off = 0; + conn->fc_crypto_enc_level = enc_level; + LSQ_DEBUG("reset handshake stream offsets, new enc level %u", + (unsigned) enc_level); + } + + stream_frame->packet_in = lsquic_packet_in_get(packet_in); + if (0 != lsquic_stream_frame_in(stream, stream_frame)) + { + ABORT_ERROR("cannot insert stream frame"); + return 0; + } + + if ((stream->sm_qflags & SMQF_WANT_READ) + && !(conn->fc_flags & FC_SERVER) + && !(conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)) + { + /* XXX what happens for server? */ + lsquic_stream_dispatch_read_events(stream); + } + + return parsed_len; +} + + static unsigned process_invalid_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in, const unsigned char *p, size_t len) @@ -2134,13 +2263,12 @@ process_rst_stream_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in, return parsed_len; } - if (lsquic_stream_id_is_critical(conn->fc_flags & FC_HTTP, stream_id)) + stream = find_stream_by_id(conn, stream_id); + if (stream && lsquic_stream_is_critical(stream)) { ABORT_ERROR("received reset on static stream %"PRIu64, stream_id); return 0; } - - stream = find_stream_by_id(conn, stream_id); if (!stream) { if (conn_is_stream_closed(conn, stream_id)) @@ -2218,6 +2346,7 @@ static process_frame_f const process_frames[N_QUIC_FRAMES] = [QUIC_FRAME_ACK] = process_ack_frame, [QUIC_FRAME_BLOCKED] = process_blocked_frame, [QUIC_FRAME_CONNECTION_CLOSE] = process_connection_close_frame, + [QUIC_FRAME_CRYPTO] = process_crypto_frame, [QUIC_FRAME_GOAWAY] = process_goaway_frame, [QUIC_FRAME_INVALID] = process_invalid_frame, [QUIC_FRAME_PADDING] = process_padding_frame, @@ -2368,8 +2497,11 @@ process_regular_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in) enum quic_ft_bit frame_types; int was_missing; - reconstruct_packet_number(conn, packet_in); - EV_LOG_PACKET_IN(LSQUIC_LOG_CONN_ID, packet_in); + if (conn->fc_conn.cn_version < LSQVER_050) + { + reconstruct_packet_number(conn, packet_in); + EV_LOG_PACKET_IN(LSQUIC_LOG_CONN_ID, packet_in); + } #if LSQUIC_CONN_STATS ++conn->fc_stats.in.packets; @@ -2398,6 +2530,9 @@ process_regular_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in) } } + if (conn->fc_conn.cn_version >= LSQVER_050) + EV_LOG_PACKET_IN(LSQUIC_LOG_CONN_ID, packet_in); + st = lsquic_rechist_received(&conn->fc_rechist, packet_in->pi_packno, packet_in->pi_received); switch (st) { @@ -2618,8 +2753,7 @@ maybe_close_conn (struct full_conn *conn) el = lsquic_hash_next(conn->fc_pub.all_streams)) { stream = lsquic_hashelem_getdata(el); - assert(LSQUIC_GQUIC_STREAM_HANDSHAKE == stream->id - || LSQUIC_GQUIC_STREAM_HEADERS == stream->id); + assert(stream->sm_bflags & (SMBF_CRYPTO|SMBF_HEADERS)); } #endif conn->fc_flags |= FC_RECV_CLOSE; /* Fake -- trigger "ok to close" */ @@ -3124,7 +3258,7 @@ process_hsk_stream_read_events (struct full_conn *conn) { lsquic_stream_t *stream; TAILQ_FOREACH(stream, &conn->fc_pub.read_streams, next_read_stream) - if (LSQUIC_GQUIC_STREAM_HANDSHAKE == stream->id) + if (lsquic_stream_is_crypto(stream)) { lsquic_stream_dispatch_read_events(stream); break; @@ -3137,7 +3271,7 @@ process_hsk_stream_write_events (struct full_conn *conn) { lsquic_stream_t *stream; TAILQ_FOREACH(stream, &conn->fc_pub.write_streams, next_write_stream) - if (LSQUIC_GQUIC_STREAM_HANDSHAKE == stream->id) + if (lsquic_stream_is_crypto(stream)) { lsquic_stream_dispatch_write_events(stream); break; @@ -4270,7 +4404,7 @@ full_conn_ci_is_tickable (lsquic_conn_t *lconn) { TAILQ_FOREACH(stream, &conn->fc_pub.write_streams, next_write_stream) - if (LSQUIC_GQUIC_STREAM_HANDSHAKE == stream->id + if (lsquic_stream_is_crypto(stream) && lsquic_stream_write_avail(stream)) { LSQ_DEBUG("tickable: stream %"PRIu64" can be written to", @@ -4398,7 +4532,7 @@ lsquic_gquic_full_conn_srej (struct lsquic_conn *lconn) &conn->fc_ver_neg, &conn->fc_pub, 0); /* Reset handshake stream state */ - stream = find_stream_by_id(conn, LSQUIC_GQUIC_STREAM_HANDSHAKE); + stream = find_stream_by_id(conn, hsk_stream_id(conn)); if (!stream) return -1; stream->n_unacked = 0; diff --git a/src/liblsquic/lsquic_handshake.c b/src/liblsquic/lsquic_handshake.c index dc8df9c..9bc23cd 100644 --- a/src/liblsquic/lsquic_handshake.c +++ b/src/liblsquic/lsquic_handshake.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "lsquic.h" @@ -35,7 +36,6 @@ #include "lsquic_mm.h" #include "lsquic_engine_public.h" #include "lsquic_hash.h" -#include "lsquic_buf.h" #include "lsquic_qtags.h" #include "lsquic_byteswap.h" #include "lsquic_sizes.h" @@ -45,6 +45,14 @@ #include "lsquic_packet_out.h" #include "lsquic_packet_in.h" #include "lsquic_handshake.h" +#include "lsquic_hkdf.h" +#include "lsquic_packet_ietf.h" + +#if __GNUC__ +# define UNLIKELY(cond) __builtin_expect(cond, 0) +#else +# define UNLIKELY(cond) cond +#endif #include "fiu-local.h" @@ -69,6 +77,14 @@ static struct conn_cid_elem dummy_cce; static const struct lsquic_conn dummy_lsquic_conn = { .cn_cces = &dummy_cce, }; static const struct lsquic_conn *const lconn = &dummy_lsquic_conn; +static const int s_log_seal_and_open; +static char s_str[0x1000]; + +static const unsigned char salt_Q050[] = { + 0x50, 0x45, 0x74, 0xEF, 0xD0, 0x66, 0xFE, 0x2F, 0x9D, 0x94, + 0x5C, 0xFC, 0xDB, 0xD3, 0xA7, 0xF0, 0xD3, 0xB5, 0x6B, 0x45, +}; + enum handshake_state { HSK_CHLO_REJ = 0, @@ -209,6 +225,14 @@ struct lsquic_zero_rtt_storage +/* gQUIC crypto has three crypto levels. */ +enum gel { GEL_CLEAR, GEL_EARLY, GEL_FORW, N_GELS /* Angels! */ }; + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define IQUIC_IV_LEN 12 +#define IQUIC_HP_LEN 16 +#define MAX_IV_LEN MAX(aes128_iv_len, IQUIC_IV_LEN) + struct lsquic_enc_session { struct lsquic_conn *es_conn; @@ -218,6 +242,8 @@ struct lsquic_enc_session ES_RECV_REJ = 1 << 1, ES_RECV_SREJ = 1 << 2, ES_FREE_CERT_PTR = 1 << 3, + ES_LOG_SECRETS = 1 << 4, + ES_GQUIC2 = 1 << 5, } es_flags; uint8_t have_key; /* 0, no 1, I, 2, D, 3, F */ @@ -226,19 +252,24 @@ struct lsquic_enc_session lsquic_cid_t cid; unsigned char priv_key[32]; - EVP_AEAD_CTX *enc_ctx_i; - EVP_AEAD_CTX *dec_ctx_i; - + /* Have to save the initial key for diversification need */ unsigned char enc_key_i[aes128_key_len]; unsigned char dec_key_i[aes128_key_len]; - unsigned char enc_key_nonce_i[aes128_iv_len]; - unsigned char dec_key_nonce_i[aes128_iv_len]; - EVP_AEAD_CTX *enc_ctx_f; - EVP_AEAD_CTX *dec_ctx_f; - unsigned char enc_key_nonce_f[aes128_iv_len]; - unsigned char dec_key_nonce_f[aes128_iv_len]; +#define enc_ctx_i es_aead_ctxs[GEL_EARLY][0] +#define dec_ctx_i es_aead_ctxs[GEL_EARLY][1] +#define enc_ctx_f es_aead_ctxs[GEL_FORW][0] +#define dec_ctx_f es_aead_ctxs[GEL_FORW][1] + EVP_AEAD_CTX *es_aead_ctxs[N_GELS][2]; + +#define enc_key_nonce_i es_ivs[GEL_EARLY][0] +#define dec_key_nonce_i es_ivs[GEL_EARLY][1] +#define enc_key_nonce_f es_ivs[GEL_FORW][0] +#define dec_key_nonce_f es_ivs[GEL_FORW][1] + unsigned char es_ivs[N_GELS][2][MAX_IV_LEN]; + + unsigned char es_hps[N_GELS][2][IQUIC_HP_LEN]; hs_ctx_t hs_ctx; lsquic_session_cache_info_t *info; @@ -255,6 +286,8 @@ struct lsquic_enc_session eshist_idx_t es_hist_idx; unsigned char es_hist_buf[1 << ESHIST_BITS]; #endif + /* The remaining fields in the struct are used for Q050+ crypto */ + lsquic_packno_t es_max_packno; }; @@ -321,7 +354,7 @@ static int init_hs_hash_tables(int flags); static uint32_t get_tag_value_i32(unsigned char *, int); static uint64_t get_tag_value_i64(unsigned char *, int); -static void determine_keys(struct lsquic_enc_session *enc_session, int is_client); +static void determine_keys(struct lsquic_enc_session *enc_session); #if LSQUIC_KEEP_ENC_SESS_HISTORY @@ -685,6 +718,125 @@ lsquic_enc_session_deserialize_zero_rtt( } +#define KEY_LABEL "quic key" +#define KEY_LABEL_SZ (sizeof(KEY_LABEL) - 1) +#define IV_LABEL "quic iv" +#define IV_LABEL_SZ (sizeof(IV_LABEL) - 1) +#define PN_LABEL "quic hp" +#define PN_LABEL_SZ (sizeof(PN_LABEL) - 1) + + +static int +gquic2_init_crypto_ctx (struct lsquic_enc_session *enc_session, + unsigned idx, const unsigned char *secret, size_t secret_sz) +{ + const EVP_MD *const md = EVP_sha256(); + const EVP_AEAD *const aead = EVP_aead_aes_128_gcm(); + unsigned char key[aes128_key_len]; + char hexbuf[sizeof(key) * 2 + 1]; + + lsquic_qhkdf_expand(md, secret, secret_sz, KEY_LABEL, KEY_LABEL_SZ, + key, sizeof(key)); + if (enc_session->es_flags & ES_LOG_SECRETS) + LSQ_DEBUG("handshake key idx %u: %s", idx, + HEXSTR(key, sizeof(key), hexbuf)); + lsquic_qhkdf_expand(md, secret, secret_sz, IV_LABEL, IV_LABEL_SZ, + enc_session->es_ivs[GEL_CLEAR][idx], IQUIC_IV_LEN); + lsquic_qhkdf_expand(md, secret, secret_sz, PN_LABEL, PN_LABEL_SZ, + enc_session->es_hps[GEL_CLEAR][idx], IQUIC_HP_LEN); + assert(!enc_session->es_aead_ctxs[GEL_CLEAR][idx]); + enc_session->es_aead_ctxs[GEL_CLEAR][idx] + = malloc(sizeof(*enc_session->es_aead_ctxs[GEL_CLEAR][idx])); + if (!enc_session->es_aead_ctxs[GEL_CLEAR][idx]) + return -1; + if (!EVP_AEAD_CTX_init(enc_session->es_aead_ctxs[GEL_CLEAR][idx], aead, + key, sizeof(key), IQUIC_TAG_LEN, NULL)) + { + free(enc_session->es_aead_ctxs[GEL_CLEAR][idx]); + enc_session->es_aead_ctxs[GEL_CLEAR][idx] = NULL; + return -1; + } + return 0; +} + + +static void +log_crypto_ctx (const struct lsquic_enc_session *enc_session, + enum enc_level enc_level, int idx) +{ + char hexbuf[EVP_MAX_MD_SIZE * 2 + 1]; + + LSQ_DEBUG("%s keys for level %s", lsquic_enclev2str[enc_level], + idx == 0 ? "encrypt" : "decrypt"); + LSQ_DEBUG("iv: %s", + HEXSTR(enc_session->es_ivs[enc_level][idx], IQUIC_IV_LEN, hexbuf)); + LSQ_DEBUG("hp: %s", + HEXSTR(enc_session->es_hps[enc_level][idx], IQUIC_HP_LEN, hexbuf)); +} + + +static int +gquic2_setup_handshake_keys (struct lsquic_enc_session *enc_session) +{ + const unsigned char *const cid_buf = enc_session->es_conn->cn_cid.idbuf; + const size_t cid_buf_sz = enc_session->es_conn->cn_cid.len; + size_t hsk_secret_sz; + int i, idx; + const EVP_MD *const md = EVP_sha256(); + const char *const labels[] = { CLIENT_LABEL, SERVER_LABEL, }; + const size_t label_sizes[] = { CLIENT_LABEL_SZ, SERVER_LABEL_SZ, }; + const unsigned dirs[2] = { + (enc_session->es_flags & ES_SERVER), + !(enc_session->es_flags & ES_SERVER), + }; + unsigned char hsk_secret[EVP_MAX_MD_SIZE]; + unsigned char secret[SHA256_DIGEST_LENGTH]; + + if (!HKDF_extract(hsk_secret, &hsk_secret_sz, md, cid_buf, cid_buf_sz, + salt_Q050, sizeof(salt_Q050))) + { + LSQ_WARN("HKDF extract failed"); + return -1; + } + + for (i = 0; i < 2; ++i) + { + idx = dirs[i]; + lsquic_qhkdf_expand(md, hsk_secret, hsk_secret_sz, labels[idx], + label_sizes[idx], secret, sizeof(secret)); + /* + LSQ_DEBUG("`%s' handshake secret: %s", + HEXSTR(secret, sizeof(secret), hexbuf)); + */ + if (0 != gquic2_init_crypto_ctx(enc_session, i, + secret, sizeof(secret))) + goto err; + if (enc_session->es_flags & ES_LOG_SECRETS) + log_crypto_ctx(enc_session, ENC_LEV_CLEAR, i); + } + + return 0; + + err: + return -1; +} + + +static void +maybe_log_secrets (struct lsquic_enc_session *enc_session) +{ + const char *log; + log = getenv("LSQUIC_LOG_SECRETS"); + if (log) + { + if (atoi(log)) + enc_session->es_flags |= ES_LOG_SECRETS; + LSQ_DEBUG("will %slog secrets", + enc_session->es_flags & ES_LOG_SECRETS ? "" : "not "); + } +} + + static enc_session_t * lsquic_enc_session_create_client (struct lsquic_conn *lconn, const char *domain, lsquic_cid_t cid, const struct lsquic_engine_public *enpub, @@ -752,6 +904,12 @@ lsquic_enc_session_create_client (struct lsquic_conn *lconn, const char *domain, enc_session->info = info; /* FIXME: allocation may fail */ lsquic_str_append(&enc_session->hs_ctx.sni, domain, strlen(domain)); + maybe_log_secrets(enc_session); + if (lconn->cn_version >= LSQVER_050) + { + enc_session->es_flags |= ES_GQUIC2; + gquic2_setup_handshake_keys(enc_session); + } return enc_session; } @@ -773,6 +931,12 @@ lsquic_enc_session_create_server (struct lsquic_conn *lconn, lsquic_cid_t cid, enc_session->enpub = enpub; enc_session->cid = cid; enc_session->es_flags |= ES_SERVER; + maybe_log_secrets(enc_session); + if (lconn->cn_version >= LSQVER_050) + { + enc_session->es_flags |= ES_GQUIC2; + gquic2_setup_handshake_keys(enc_session); + } return enc_session; } @@ -792,6 +956,9 @@ static void lsquic_enc_session_destroy (enc_session_t *enc_session_p) { struct lsquic_enc_session *const enc_session = enc_session_p; + enum gel gel; + unsigned i; + if (!enc_session) return ; @@ -810,26 +977,14 @@ lsquic_enc_session_destroy (enc_session_t *enc_session_p) lsquic_str_d(&enc_session->chlo); lsquic_str_d(&enc_session->sstk); lsquic_str_d(&enc_session->ssno); - if (enc_session->dec_ctx_i) - { - EVP_AEAD_CTX_cleanup(enc_session->dec_ctx_i); - free(enc_session->dec_ctx_i); - } - if (enc_session->enc_ctx_i) - { - EVP_AEAD_CTX_cleanup(enc_session->enc_ctx_i); - free(enc_session->enc_ctx_i); - } - if (enc_session->dec_ctx_f) - { - EVP_AEAD_CTX_cleanup(enc_session->dec_ctx_f); - free(enc_session->dec_ctx_f); - } - if (enc_session->enc_ctx_f) - { - EVP_AEAD_CTX_cleanup(enc_session->enc_ctx_f); - free(enc_session->enc_ctx_f); - } + for (gel = 0; gel < N_GELS; ++gel) + for (i = 0; i < 2; ++i) + if (enc_session->es_aead_ctxs[gel][i]) + { + EVP_AEAD_CTX_cleanup(enc_session->es_aead_ctxs[gel][i]); + free(enc_session->es_aead_ctxs[gel][i]); + } + memset(enc_session->es_aead_ctxs, 0, sizeof(enc_session->es_aead_ctxs)); if (enc_session->info) { lsquic_str_d(&enc_session->info->sstk); @@ -1509,7 +1664,7 @@ lsquic_enc_session_gen_chlo (enc_session_t *enc_session_p, { enc_session->have_key = 0; assert(lsquic_str_len(enc_session->cert_ptr) > 0); - determine_keys(enc_session, 1); + determine_keys(enc_session); enc_session->have_key = 1; } @@ -2243,11 +2398,14 @@ static int handle_chlo_reply_verify_prof(struct lsquic_enc_session *enc_session, } -void setup_aead_ctx(EVP_AEAD_CTX **ctx, unsigned char key[], int key_len, - unsigned char *key_copy) +static void +setup_aead_ctx (const struct lsquic_enc_session *enc_session, + EVP_AEAD_CTX **ctx, unsigned char key[], int key_len, + unsigned char *key_copy) { const EVP_AEAD *aead_ = EVP_aead_aes_128_gcm(); - const int auth_tag_size = 12; + const int auth_tag_size = enc_session->es_flags & ES_GQUIC2 + ? IQUIC_TAG_LEN : GQUIC_PACKET_HASH_SZ; if (*ctx) { EVP_AEAD_CTX_cleanup(*ctx); @@ -2263,14 +2421,16 @@ void setup_aead_ctx(EVP_AEAD_CTX **ctx, unsigned char key[], int key_len, static int determine_diversification_key (enc_session_t *enc_session_p, - uint8_t *diversification_nonce - , int is_client - ) + uint8_t *diversification_nonce) { struct lsquic_enc_session *const enc_session = enc_session_p; + const int is_client = !(enc_session->es_flags & ES_SERVER); EVP_AEAD_CTX **ctx_s_key; unsigned char *key_i, *iv; - uint8_t ikm[aes128_key_len + aes128_iv_len]; + const size_t iv_len = enc_session->es_flags & ES_GQUIC2 + ? IQUIC_IV_LEN : aes128_iv_len; + uint8_t ikm[aes128_key_len + MAX_IV_LEN]; + char str_buf[DNONC_LENGTH * 2 + 1]; if (is_client) { @@ -2285,30 +2445,38 @@ determine_diversification_key (enc_session_t *enc_session_p, iv = enc_session->enc_key_nonce_i; } memcpy(ikm, key_i, aes128_key_len); - memcpy(ikm + aes128_key_len, iv, aes128_iv_len); - export_key_material(ikm, aes128_key_len + aes128_iv_len, + memcpy(ikm + aes128_key_len, iv, iv_len); + lsquic_export_key_material(ikm, aes128_key_len + iv_len, diversification_nonce, DNONC_LENGTH, (const unsigned char *) "QUIC key diversification", 24, 0, NULL, aes128_key_len, key_i, 0, NULL, - aes128_iv_len, iv, NULL); + iv_len, iv, NULL, NULL, NULL); - setup_aead_ctx(ctx_s_key, key_i, aes128_key_len, NULL); - LSQ_DEBUG("determine_diversification_keys diversification_key: %s\n", - get_bin_str(key_i, aes128_key_len, 512)); - LSQ_DEBUG("determine_diversification_keys diversification_key nonce: %s\n", - get_bin_str(iv, aes128_iv_len, 512)); + setup_aead_ctx(enc_session, ctx_s_key, key_i, aes128_key_len, NULL); + if (enc_session->es_flags & ES_LOG_SECRETS) + { + LSQ_DEBUG("determine_diversification_keys nonce: %s", + HEXSTR(diversification_nonce, DNONC_LENGTH, str_buf)); + LSQ_DEBUG("determine_diversification_keys diversification key: %s", + HEXSTR(key_i, aes128_key_len, str_buf)); + LSQ_DEBUG("determine_diversification_keys diversification iv: %s", + HEXSTR(iv, iv_len, str_buf)); + } return 0; } /* After CHLO msg generatered, call it to determine_keys */ static void -determine_keys (struct lsquic_enc_session *enc_session, int is_client) +determine_keys (struct lsquic_enc_session *enc_session) { lsquic_str_t *chlo = &enc_session->chlo; + const int is_client = !(enc_session->es_flags & ES_SERVER); uint8_t shared_key_c[32]; - struct lsquic_buf *nonce_c = lsquic_buf_create(100); - struct lsquic_buf *hkdf_input = lsquic_buf_create(0); + const size_t iv_len = enc_session->es_flags & ES_GQUIC2 + ? IQUIC_IV_LEN : aes128_iv_len; + unsigned char *nonce_c; + unsigned char *hkdf_input, *hkdf_input_p; unsigned char c_key[aes128_key_len]; unsigned char s_key[aes128_key_len]; @@ -2317,20 +2485,38 @@ determine_keys (struct lsquic_enc_session *enc_session, int is_client) unsigned char *c_iv; unsigned char *s_iv; + uint8_t *c_hp, *s_hp; + size_t nonce_len, hkdf_input_len; unsigned char sub_key[32]; EVP_AEAD_CTX **ctx_c_key, **ctx_s_key; char key_flag; + char str_buf[512]; + + hkdf_input_len = (enc_session->have_key == 0 ? 18 + 1 : 33 + 1) + + enc_session->cid.len + + lsquic_str_len(chlo) + + (is_client + ? lsquic_str_len(&enc_session->info->scfg) + : (size_t) enc_session->server_config->lsc_scfg->info.scfg_len) + + lsquic_str_len(enc_session->cert_ptr); + hkdf_input = malloc(hkdf_input_len); + if (UNLIKELY(!hkdf_input)) + { + LSQ_WARN("cannot allocate memory for hkdf_input"); + return; + } + hkdf_input_p = hkdf_input; - lsquic_buf_clear(nonce_c); - lsquic_buf_clear(hkdf_input); if (enc_session->have_key == 0) { - lsquic_buf_append(hkdf_input, "QUIC key expansion\0", 18 + 1); // Add a 0x00 */ + memcpy(hkdf_input_p, "QUIC key expansion\0", 18 + 1); /* Add a 0x00 */ + hkdf_input_p += 18 + 1; key_flag = 'I'; } else { - lsquic_buf_append(hkdf_input, "QUIC forward secure key expansion\0", 33 + 1); // Add a 0x00 */ + memcpy(hkdf_input_p, "QUIC forward secure key expansion\0", 33 + 1); /* Add a 0x00 */ + hkdf_input_p += 33 + 1; key_flag = 'F'; } @@ -2347,6 +2533,10 @@ determine_keys (struct lsquic_enc_session *enc_session, int is_client) s_iv = (unsigned char *) enc_session->dec_key_nonce_i; c_key_bin = enc_session->enc_key_i; s_key_bin = enc_session->dec_key_i; + c_hp = enc_session->es_flags & ES_GQUIC2 + ? enc_session->es_hps[GEL_EARLY][0] : NULL; + s_hp = enc_session->es_flags & ES_GQUIC2 + ? enc_session->es_hps[GEL_EARLY][1] : NULL; } else { @@ -2354,6 +2544,10 @@ determine_keys (struct lsquic_enc_session *enc_session, int is_client) ctx_s_key = &enc_session->dec_ctx_f; c_iv = (unsigned char *) enc_session->enc_key_nonce_f; s_iv = (unsigned char *) enc_session->dec_key_nonce_f; + c_hp = enc_session->es_flags & ES_GQUIC2 + ? enc_session->es_hps[GEL_FORW][0] : NULL; + s_hp = enc_session->es_flags & ES_GQUIC2 + ? enc_session->es_hps[GEL_FORW][1] : NULL; } } else @@ -2366,6 +2560,10 @@ determine_keys (struct lsquic_enc_session *enc_session, int is_client) s_iv = (unsigned char *) enc_session->enc_key_nonce_i; c_key_bin = enc_session->dec_key_i; s_key_bin = enc_session->enc_key_i; + c_hp = enc_session->es_flags & ES_GQUIC2 + ? enc_session->es_hps[GEL_EARLY][1] : NULL; + s_hp = enc_session->es_flags & ES_GQUIC2 + ? enc_session->es_hps[GEL_EARLY][0] : NULL; } else { @@ -2373,67 +2571,94 @@ determine_keys (struct lsquic_enc_session *enc_session, int is_client) ctx_s_key = &enc_session->enc_ctx_f; c_iv = (unsigned char *) enc_session->dec_key_nonce_f; s_iv = (unsigned char *) enc_session->enc_key_nonce_f; + c_hp = enc_session->es_flags & ES_GQUIC2 + ? enc_session->es_hps[GEL_FORW][1] : NULL; + s_hp = enc_session->es_flags & ES_GQUIC2 + ? enc_session->es_hps[GEL_FORW][0] : NULL; } } LSQ_DEBUG("export_key_material c255_gen_share_key %s", get_bin_str(shared_key_c, 32, 512)); - lsquic_buf_append(hkdf_input, (const char *) enc_session->cid.idbuf, - enc_session->cid.len); - lsquic_buf_append(hkdf_input, lsquic_str_cstr(chlo), lsquic_str_len(chlo)); /* CHLO msg */ + memcpy(hkdf_input_p, enc_session->cid.idbuf, enc_session->cid.len); + hkdf_input_p += enc_session->cid.len; + memcpy(hkdf_input_p, lsquic_str_cstr(chlo), lsquic_str_len(chlo)); /* CHLO msg */ + hkdf_input_p += lsquic_str_len(chlo); if (is_client) { - lsquic_buf_append(hkdf_input, lsquic_str_cstr(&enc_session->info->scfg), + memcpy(hkdf_input_p, lsquic_str_cstr(&enc_session->info->scfg), lsquic_str_len(&enc_session->info->scfg)); /* scfg msg */ + hkdf_input_p += lsquic_str_len(&enc_session->info->scfg); } else { - lsquic_buf_append(hkdf_input, + memcpy(hkdf_input_p, (const char *) enc_session->server_config->lsc_scfg->scfg, enc_session->server_config->lsc_scfg->info.scfg_len); + hkdf_input_p += enc_session->server_config->lsc_scfg->info.scfg_len; } - lsquic_buf_append(hkdf_input, lsquic_str_cstr(enc_session->cert_ptr), + memcpy(hkdf_input_p, lsquic_str_cstr(enc_session->cert_ptr), lsquic_str_len(enc_session->cert_ptr)); + assert(hkdf_input + hkdf_input_len + == hkdf_input_p + lsquic_str_len(enc_session->cert_ptr)); LSQ_DEBUG("export_key_material hkdf_input %s", - get_bin_str(lsquic_buf_begin(hkdf_input), - (size_t)lsquic_buf_size(hkdf_input), 512)); + HEXSTR(hkdf_input, hkdf_input_len, str_buf)); /* then need to use the salts and the shared_key_* to get the real aead key */ - lsquic_buf_append(nonce_c, (const char *) enc_session->hs_ctx.nonc, 32); - lsquic_buf_append(nonce_c, lsquic_str_cstr(&enc_session->ssno), - lsquic_str_len(&enc_session->ssno)); - LSQ_DEBUG("export_key_material nonce %s", - get_bin_str(lsquic_buf_begin(nonce_c), - (size_t)lsquic_buf_size(nonce_c), 512)); + nonce_len = sizeof(enc_session->hs_ctx.nonc) + + lsquic_str_len(&enc_session->ssno); + nonce_c = malloc(nonce_len); + if (UNLIKELY(!nonce_c)) + { + LSQ_WARN("cannot allocate memory for nonce_c"); + free(hkdf_input); + return; + } + memcpy(nonce_c, enc_session->hs_ctx.nonc, sizeof(enc_session->hs_ctx.nonc)); + memcpy(nonce_c + sizeof(enc_session->hs_ctx.nonc), + lsquic_str_cstr(&enc_session->ssno), + lsquic_str_len(&enc_session->ssno)); - export_key_material(shared_key_c, 32, - (unsigned char *)lsquic_buf_begin(nonce_c), lsquic_buf_size(nonce_c), - (unsigned char *)lsquic_buf_begin(hkdf_input), - lsquic_buf_size(hkdf_input), + LSQ_DEBUG("export_key_material nonce %s", + HEXSTR(nonce_c, nonce_len, str_buf)); + + lsquic_export_key_material(shared_key_c, 32, + nonce_c, nonce_len, + hkdf_input, hkdf_input_len, aes128_key_len, c_key, aes128_key_len, s_key, - aes128_iv_len, c_iv, - aes128_iv_len, s_iv, - sub_key); + iv_len, c_iv, + iv_len, s_iv, + sub_key, + c_hp, s_hp + ); - setup_aead_ctx(ctx_c_key, c_key, aes128_key_len, c_key_bin); - setup_aead_ctx(ctx_s_key, s_key, aes128_key_len, s_key_bin); + setup_aead_ctx(enc_session, ctx_c_key, c_key, aes128_key_len, c_key_bin); + setup_aead_ctx(enc_session, ctx_s_key, s_key, aes128_key_len, s_key_bin); + free(nonce_c); + free(hkdf_input); - lsquic_buf_destroy(nonce_c); - lsquic_buf_destroy(hkdf_input); - - LSQ_DEBUG("***export_key_material '%c' c_key: %s", key_flag, - get_bin_str(c_key, aes128_key_len, 512)); - LSQ_DEBUG("***export_key_material '%c' s_key: %s", key_flag, - get_bin_str(s_key, aes128_key_len, 512)); - LSQ_DEBUG("***export_key_material '%c' c_iv: %s", key_flag, - get_bin_str(c_iv, aes128_iv_len, 512)); - LSQ_DEBUG("***export_key_material '%c' s_iv: %s", key_flag, - get_bin_str(s_iv, aes128_iv_len, 512)); - LSQ_DEBUG("***export_key_material '%c' subkey: %s", key_flag, - get_bin_str(sub_key, 32, 512)); + if (enc_session->es_flags & ES_LOG_SECRETS) + { + LSQ_DEBUG("***export_key_material '%c' c_key: %s", key_flag, + HEXSTR(c_key, aes128_key_len, str_buf)); + LSQ_DEBUG("***export_key_material '%c' s_key: %s", key_flag, + HEXSTR(s_key, aes128_key_len, str_buf)); + LSQ_DEBUG("***export_key_material '%c' c_iv: %s", key_flag, + HEXSTR(c_iv, iv_len, str_buf)); + LSQ_DEBUG("***export_key_material '%c' s_iv: %s", key_flag, + HEXSTR(s_iv, iv_len, str_buf)); + LSQ_DEBUG("***export_key_material '%c' subkey: %s", key_flag, + HEXSTR(sub_key, 32, str_buf)); + if (c_hp) + LSQ_DEBUG("***export_key_material '%c' c_hp: %s", key_flag, + HEXSTR(c_hp, IQUIC_HP_LEN, str_buf)); + if (s_hp) + LSQ_DEBUG("***export_key_material '%c' s_hp: %s", key_flag, + HEXSTR(s_hp, IQUIC_HP_LEN, str_buf)); + } } @@ -2591,7 +2816,7 @@ lsquic_enc_session_handle_chlo_reply (enc_session_t *enc_session_p, if (enc_session->hsk_state == HSK_COMPLETED) { - determine_keys(enc_session , 1); + determine_keys(enc_session); enc_session->have_key = 3; } @@ -2878,7 +3103,7 @@ lsquic_enc_session_decrypt (struct lsquic_enc_session *enc_session, /* Client: got SHLO which should have diversification_nonce */ if (diversification_nonce && enc_session && enc_session->have_key == 1) { - determine_diversification_key(enc_session, diversification_nonce, 1); + determine_diversification_key(enc_session, diversification_nonce); enc_session->have_key = 2; } @@ -3079,7 +3304,7 @@ lsquic_enc_session_handle_chlo(enc_session_t *enc_session_p, else if (rtt == HS_SHLO) { enc_session->have_key = 0; - determine_keys(enc_session, 0); + determine_keys(enc_session); enc_session->have_key = 1; if (lsquic_str_len(&enc_session->hs_ctx.stk) > 0) @@ -3099,9 +3324,9 @@ lsquic_enc_session_handle_chlo(enc_session_t *enc_session_p, *out_len = len; *nonce_set = 1; - determine_diversification_key(enc_session, nonce, 0); + determine_diversification_key(enc_session, nonce); enc_session->have_key = 2; - determine_keys(enc_session, 0); + determine_keys(enc_session); enc_session->have_key = 3; enc_session->hsk_state = HSK_COMPLETED; @@ -3664,6 +3889,439 @@ struct enc_session_funcs_common lsquic_enc_session_common_gquic_1 = }; +static void +gquic2_gen_hp_mask (struct lsquic_enc_session *enc_session, + const unsigned char hp[IQUIC_HP_LEN], + const unsigned char *sample, unsigned char mask[EVP_MAX_BLOCK_LENGTH]) +{ + const EVP_CIPHER *const cipher = EVP_aes_128_ecb(); + EVP_CIPHER_CTX hp_ctx; + int out_len; + + EVP_CIPHER_CTX_init(&hp_ctx); + if (EVP_EncryptInit_ex(&hp_ctx, cipher, NULL, hp, 0) + && EVP_EncryptUpdate(&hp_ctx, mask, &out_len, sample, 16)) + { + assert(out_len >= 5); + } + else + { + LSQ_WARN("cannot generate hp mask, error code: %"PRIu32, + ERR_get_error()); + enc_session->es_conn->cn_if->ci_internal_error(enc_session->es_conn, + "cannot generate hp mask, error code: %"PRIu32, ERR_get_error()); + } + + (void) EVP_CIPHER_CTX_cleanup(&hp_ctx); + + if (0) + { + char hp_str[IQUIC_HP_LEN * 2 + 1], sample_str[16 * 2 + 1]; + LSQ_DEBUG("generated hp mask using hp %s and sample %s", + HEXSTR(hp, IQUIC_HP_LEN, hp_str), + HEXSTR(sample, 16, sample_str)); + } +} + + +static void +gquic2_apply_hp (struct lsquic_enc_session *enc_session, + enum gel gel, unsigned char *dst, unsigned packno_off, + unsigned sample_off, unsigned packno_len) +{ + unsigned char mask[EVP_MAX_BLOCK_LENGTH]; + char mask_str[5 * 2 + 1]; + + gquic2_gen_hp_mask(enc_session, enc_session->es_hps[gel][0], + dst + sample_off, mask); + LSQ_DEBUG("apply header protection using mask %s", + HEXSTR(mask, 5, mask_str)); + dst[0] ^= (0xF | (((dst[0] & 0x80) == 0) << 4)) & mask[0]; + switch (packno_len) + { + case 4: + dst[packno_off + 3] ^= mask[4]; + /* fall-through */ + case 3: + dst[packno_off + 2] ^= mask[3]; + /* fall-through */ + case 2: + dst[packno_off + 1] ^= mask[2]; + /* fall-through */ + default: + dst[packno_off + 0] ^= mask[1]; + } +} + + +static const enum gel hety2gel[] = +{ + [HETY_NOT_SET] = GEL_FORW, + [HETY_VERNEG] = 0, + [HETY_INITIAL] = GEL_CLEAR, + [HETY_RETRY] = 0, + [HETY_HANDSHAKE] = GEL_CLEAR, + [HETY_0RTT] = GEL_EARLY, +}; + + +static const char *const gel2str[] = +{ + [GEL_CLEAR] = "clear", + [GEL_EARLY] = "early", + [GEL_FORW] = "forw-secure", +}; + + +static const enum enc_level gel2el[] = +{ + [GEL_CLEAR] = ENC_LEV_CLEAR, + [GEL_EARLY] = ENC_LEV_EARLY, + [GEL_FORW] = ENC_LEV_FORW, +}; + + +static enum enc_packout +gquic2_esf_encrypt_packet (enc_session_t *enc_session_p, + const struct lsquic_engine_public *enpub, struct lsquic_conn *lconn_UNUSED, + struct lsquic_packet_out *packet_out) +{ + struct lsquic_enc_session *const enc_session = enc_session_p; + struct lsquic_conn *const lconn = enc_session->es_conn; + EVP_AEAD_CTX *aead_ctx; + unsigned char *dst; + enum gel gel; + unsigned char nonce_buf[ IQUIC_IV_LEN + 8 ]; + unsigned char *nonce, *begin_xor; + lsquic_packno_t packno; + size_t out_sz, dst_sz; + int header_sz; + int ipv6; + unsigned packno_off, packno_len, sample_off, divers_nonce_len; + char errbuf[ERR_ERROR_STRING_BUF_LEN]; + + gel = hety2gel[ packet_out->po_header_type ]; + aead_ctx = enc_session->es_aead_ctxs[gel][0]; + if (UNLIKELY(!aead_ctx)) + { + LSQ_WARN("encrypt crypto context at level %s not initialized", + gel2str[gel]); + return ENCPA_BADCRYPT; + } + + if (packet_out->po_data_sz < 3) + { + /* [draft-ietf-quic-tls-20] Section 5.4.2 */ + enum packno_bits bits = lsquic_packet_out_packno_bits(packet_out); + /* XXX same packet rules as in IETF QUIC? */ + unsigned len = iquic_packno_bits2len(bits); + if (packet_out->po_data_sz + len < 4) + { + len = 4 - packet_out->po_data_sz - len; + memset(packet_out->po_data + packet_out->po_data_sz, 0, len); + packet_out->po_data_sz += len; + packet_out->po_frame_types |= QUIC_FTBIT_PADDING; + LSQ_DEBUG("padded packet %"PRIu64" with %u bytes of PADDING", + packet_out->po_packno, len); + } + } + + dst_sz = lconn->cn_pf->pf_packout_size(lconn, packet_out); + ipv6 = NP_IS_IPv6(packet_out->po_path); + dst = enpub->enp_pmi->pmi_allocate(enpub->enp_pmi_ctx, + packet_out->po_path->np_peer_ctx, dst_sz, ipv6); + if (!dst) + { + LSQ_DEBUG("could not allocate memory for outgoing packet of size %zd", + dst_sz); + return ENCPA_NOMEM; + } + + /* Align nonce so we can perform XOR safely in one shot: */ + begin_xor = nonce_buf + sizeof(nonce_buf) - 8; + begin_xor = (unsigned char *) ((uintptr_t) begin_xor & ~0x7); + nonce = begin_xor - IQUIC_IV_LEN + 8; + memcpy(nonce, enc_session->es_ivs[gel][0], IQUIC_IV_LEN); + packno = packet_out->po_packno; +#if __BYTE_ORDER == __LITTLE_ENDIAN + packno = bswap_64(packno); +#endif + *((uint64_t *) begin_xor) ^= packno; + + header_sz = lconn->cn_pf->pf_gen_reg_pkt_header(lconn, packet_out, dst, + dst_sz); + if (header_sz < 0) + goto err; + + if (s_log_seal_and_open) + { + LSQ_DEBUG("seal: iv (%u bytes): %s", IQUIC_IV_LEN, + HEXSTR(nonce, IQUIC_IV_LEN, s_str)); + LSQ_DEBUG("seal: ad (%u bytes): %s", header_sz, + HEXSTR(dst, header_sz, s_str)); + LSQ_DEBUG("seal: in (%hu bytes): %s", packet_out->po_data_sz, + HEXSTR(packet_out->po_data, packet_out->po_data_sz, s_str)); + } + + if (!EVP_AEAD_CTX_seal(aead_ctx, dst + header_sz, &out_sz, + dst_sz - header_sz, nonce, IQUIC_IV_LEN, + packet_out->po_data, packet_out->po_data_sz, dst, header_sz)) + { + LSQ_WARN("cannot seal packet #%"PRIu64": %s", packet_out->po_packno, + ERR_error_string(ERR_get_error(), errbuf)); + goto err; + } + assert(out_sz == dst_sz - header_sz); + + lconn->cn_pf->pf_packno_info(lconn, packet_out, &packno_off, &packno_len); + if (!packet_out->po_nonce) + divers_nonce_len = 0; + else + { + assert(enc_session->es_flags & ES_SERVER); + assert(gel == GEL_EARLY); + divers_nonce_len = DNONC_LENGTH; + } + sample_off = packno_off + divers_nonce_len + 4; + assert(sample_off + IQUIC_TAG_LEN <= dst_sz); + gquic2_apply_hp(enc_session, gel, dst, packno_off, sample_off, packno_len); + + packet_out->po_enc_data = dst; + packet_out->po_enc_data_sz = dst_sz; + packet_out->po_sent_sz = dst_sz; + packet_out->po_flags &= ~PO_IPv6; + packet_out->po_flags |= PO_ENCRYPTED|PO_SENT_SZ|(ipv6 << POIPv6_SHIFT); + lsquic_packet_out_set_enc_level(packet_out, gel2el[gel]); + return ENCPA_OK; + + err: + enpub->enp_pmi->pmi_return(enpub->enp_pmi_ctx, + packet_out->po_path->np_peer_ctx, dst, ipv6); + return ENCPA_BADCRYPT; +} + + +/* XXX this is an exact copy, can reuse */ +static lsquic_packno_t +decode_packno (lsquic_packno_t max_packno, lsquic_packno_t packno, + unsigned shift) +{ + lsquic_packno_t candidates[3], epoch_delta; + int64_t diffs[3]; + unsigned min;; + + epoch_delta = 1ULL << shift; + candidates[1] = (max_packno & ~(epoch_delta - 1)) + packno; + candidates[0] = candidates[1] - epoch_delta; + candidates[2] = candidates[1] + epoch_delta; + + diffs[0] = llabs((int64_t) candidates[0] - (int64_t) max_packno); + diffs[1] = llabs((int64_t) candidates[1] - (int64_t) max_packno); + diffs[2] = llabs((int64_t) candidates[2] - (int64_t) max_packno); + + min = diffs[1] < diffs[0]; + if (diffs[2] < diffs[min]) + min = 2; + + return candidates[min]; +} + + +static lsquic_packno_t +gquic2_strip_hp (struct lsquic_enc_session *enc_session, + enum gel gel, const unsigned char *iv, unsigned char *dst, + unsigned packno_off, unsigned *packno_len) +{ + lsquic_packno_t packno; + unsigned shift; + unsigned char mask[EVP_MAX_BLOCK_LENGTH]; + char mask_str[5 * 2 + 1]; + + gquic2_gen_hp_mask(enc_session, enc_session->es_hps[gel][1], iv, mask); + LSQ_DEBUG("strip header protection using mask %s", + HEXSTR(mask, 5, mask_str)); + dst[0] ^= (0xF | (((dst[0] & 0x80) == 0) << 4)) & mask[0]; + packno = 0; + shift = 0; + *packno_len = 1 + (dst[0] & 3); + switch (*packno_len) + { + case 4: + dst[packno_off + 3] ^= mask[4]; + packno |= dst[packno_off + 3]; + shift += 8; + /* fall-through */ + case 3: + dst[packno_off + 2] ^= mask[3]; + packno |= dst[packno_off + 2] << shift; + shift += 8; + /* fall-through */ + case 2: + dst[packno_off + 1] ^= mask[2]; + packno |= dst[packno_off + 1] << shift; + shift += 8; + /* fall-through */ + default: + dst[packno_off + 0] ^= mask[1]; + packno |= dst[packno_off + 0] << shift; + shift += 8; + } + return decode_packno(enc_session->es_max_packno, packno, shift); +} + + +static enum dec_packin +gquic2_esf_decrypt_packet (enc_session_t *enc_session_p, + struct lsquic_engine_public *enpub, const struct lsquic_conn *lconn, + struct lsquic_packet_in *packet_in) +{ + struct lsquic_enc_session *const enc_session = enc_session_p; + unsigned char *dst; + unsigned char nonce_buf[ IQUIC_IV_LEN + 8 ]; + unsigned char *nonce, *begin_xor; + unsigned sample_off, packno_len, divers_nonce_len; + enum gel gel; + lsquic_packno_t packno; + size_t out_sz; + enum dec_packin dec_packin; + const size_t dst_sz = packet_in->pi_data_sz; + char errbuf[ERR_ERROR_STRING_BUF_LEN]; + + dst = lsquic_mm_get_packet_in_buf(&enpub->enp_mm, dst_sz); + if (!dst) + { + LSQ_WARN("cannot allocate memory to copy incoming packet data"); + dec_packin = DECPI_NOMEM; + goto err; + } + + if (!(HETY_0RTT == packet_in->pi_header_type + && !(enc_session->es_flags & ES_SERVER))) + divers_nonce_len = 0; + else + divers_nonce_len = DNONC_LENGTH; + + gel = hety2gel[packet_in->pi_header_type]; + if (UNLIKELY(!enc_session->es_aead_ctxs[gel][1])) + { + LSQ_INFO("decrypt crypto context at level %s not initialized", + gel2str[gel]); + dec_packin = DECPI_BADCRYPT; + goto err; + } + + /* Decrypt packet number. After this operation, packet_in is adjusted: + * the packet number becomes part of the header. + */ + sample_off = packet_in->pi_header_sz + divers_nonce_len + 4; + if (sample_off + IQUIC_TAG_LEN > packet_in->pi_data_sz) + { + LSQ_INFO("packet data is too short: %hu bytes", + packet_in->pi_data_sz); + dec_packin = DECPI_TOO_SHORT; + goto err; + } + memcpy(dst, packet_in->pi_data, sample_off); + packet_in->pi_packno = + packno = gquic2_strip_hp(enc_session, gel, + packet_in->pi_data + sample_off, + dst, packet_in->pi_header_sz, &packno_len); + + packet_in->pi_header_sz += packno_len; + if (UNLIKELY(divers_nonce_len)) + { + if (enc_session->have_key == 1) + { + determine_diversification_key(enc_session, + dst + packet_in->pi_header_sz); + enc_session->have_key = 2; + } + packet_in->pi_header_sz += divers_nonce_len; + } + + /* Align nonce so we can perform XOR safely in one shot: */ + begin_xor = nonce_buf + sizeof(nonce_buf) - 8; + begin_xor = (unsigned char *) ((uintptr_t) begin_xor & ~0x7); + nonce = begin_xor - IQUIC_IV_LEN + 8; + memcpy(nonce, enc_session->es_ivs[gel][1], IQUIC_IV_LEN); +#if __BYTE_ORDER == __LITTLE_ENDIAN + packno = bswap_64(packno); +#endif + *((uint64_t *) begin_xor) ^= packno; + + if (s_log_seal_and_open) + { + LSQ_DEBUG("open: iv (%u bytes): %s", IQUIC_IV_LEN, + HEXSTR(nonce, IQUIC_IV_LEN, s_str)); + LSQ_DEBUG("open: ad (%u bytes): %s", packet_in->pi_header_sz, + HEXSTR(dst, packet_in->pi_header_sz, s_str)); + LSQ_DEBUG("open: in (%u bytes): %s", + packet_in->pi_data_sz - packet_in->pi_header_sz, + HEXSTR(packet_in->pi_data + packet_in->pi_header_sz, + packet_in->pi_data_sz - packet_in->pi_header_sz, s_str)); + } + + if (!EVP_AEAD_CTX_open(enc_session->es_aead_ctxs[gel][1], + dst + packet_in->pi_header_sz, &out_sz, + dst_sz - packet_in->pi_header_sz, nonce, IQUIC_IV_LEN, + packet_in->pi_data + packet_in->pi_header_sz, + packet_in->pi_data_sz - packet_in->pi_header_sz, + dst, packet_in->pi_header_sz)) + { + LSQ_INFO("cannot open packet #%"PRIu64": %s", packet_in->pi_packno, + ERR_error_string(ERR_get_error(), errbuf)); + dec_packin = DECPI_BADCRYPT; + goto err; + } + + /* Bits 2 and 3 are not set and don't need to be check in gQUIC */ + + packet_in->pi_data_sz = packet_in->pi_header_sz + out_sz; + if (packet_in->pi_flags & PI_OWN_DATA) + lsquic_mm_put_packet_in_buf(&enpub->enp_mm, packet_in->pi_data, + packet_in->pi_data_sz); + packet_in->pi_data = dst; + packet_in->pi_flags |= PI_OWN_DATA | PI_DECRYPTED + | (gel2el[gel] << PIBIT_ENC_LEV_SHIFT); + EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "decrypted packet %"PRIu64, + packet_in->pi_packno); + if (packet_in->pi_packno > enc_session->es_max_packno) + enc_session->es_max_packno = packet_in->pi_packno; + return DECPI_OK; + + err: + if (dst) + lsquic_mm_put_packet_in_buf(&enpub->enp_mm, dst, dst_sz); + EV_LOG_CONN_EVENT(LSQUIC_LOG_CONN_ID, "could not decrypt packet (type %s, " + "number %"PRIu64")", lsquic_hety2str[packet_in->pi_header_type], + packet_in->pi_packno); + return dec_packin; +} + + +#ifdef NDEBUG +const +#endif +/* Q050 and later */ +struct enc_session_funcs_common lsquic_enc_session_common_gquic_2 = +{ + .esf_global_init = lsquic_handshake_init, + .esf_global_cleanup = lsquic_handshake_cleanup, + .esf_cipher = lsquic_enc_session_cipher, + .esf_keysize = lsquic_enc_session_keysize, + .esf_alg_keysize = lsquic_enc_session_alg_keysize, + .esf_get_server_cert_chain = lsquic_enc_session_get_server_cert_chain, + .esf_verify_reset_token = lsquic_enc_session_verify_reset_token, + .esf_did_zero_rtt_succeed = lsquic_enc_session_did_zero_rtt_succeed, + .esf_is_zero_rtt_enabled = lsquic_enc_session_is_zero_rtt_enabled, + .esf_set_conn = gquic_esf_set_conn, + /* These are different from gquic_1: */ + .esf_encrypt_packet = gquic2_esf_encrypt_packet, + .esf_decrypt_packet = gquic2_esf_decrypt_packet, + .esf_tag_len = IQUIC_TAG_LEN, +}; + + #ifdef NDEBUG const #endif diff --git a/src/liblsquic/lsquic_hspack_valid.c b/src/liblsquic/lsquic_hspack_valid.c index 1b196aa..a74406f 100644 --- a/src/liblsquic/lsquic_hspack_valid.c +++ b/src/liblsquic/lsquic_hspack_valid.c @@ -121,7 +121,8 @@ lsquic_is_valid_hs_packet (struct lsquic_engine *engine, case 0x80|0x00|0x20|0x00|0x08: case 0x80|0x40|0x20|0x00|0x00: case 0x80|0x00|0x20|0x00|0x00: - is_valid = lsquic_is_valid_ietf_v1_or_Q046_hs_packet(buf, bufsz, &tag); + is_valid = lsquic_is_valid_ietf_v1_or_Q046plus_hs_packet(buf, bufsz, + &tag); break; /* 01XX XGGG: ID-22 short header */ case 0x00|0x40|0x00|0x00|0x00: diff --git a/src/liblsquic/lsquic_mini_conn.c b/src/liblsquic/lsquic_mini_conn.c index 5a11e8c..5dd4406 100644 --- a/src/liblsquic/lsquic_mini_conn.c +++ b/src/liblsquic/lsquic_mini_conn.c @@ -59,6 +59,7 @@ static const struct conn_iface mini_conn_iface_standard; +static const struct conn_iface mini_conn_iface_standard_Q050; #if LSQUIC_ENABLE_HANDSHAKE_DISABLE static const struct conn_iface mini_conn_iface_disable_handshake; #endif @@ -138,6 +139,14 @@ lowest_bit_set (unsigned v) } +static int +is_handshake_stream_id (const struct mini_conn *conn, + lsquic_stream_id_t stream_id) +{ + return conn->mc_conn.cn_version < LSQVER_050 && stream_id == 1; +} + + static void mini_destroy_packet (struct mini_conn *mc, struct lsquic_packet_out *packet_out) { @@ -183,7 +192,15 @@ mini_conn_new (struct lsquic_engine_public *enp, { if (!packet_in_is_ok(packet_in)) return NULL; - conn_iface = &mini_conn_iface_standard; + switch (version) + { + case LSQVER_050: + conn_iface = &mini_conn_iface_standard_Q050; + break; + default: + conn_iface = &mini_conn_iface_standard; + break; + } } mc = lsquic_malo_get(enp->enp_mm.malo.mini_conn); @@ -469,7 +486,7 @@ process_rst_stream_frame (struct mini_conn *mc, lsquic_packet_in_t *packet_in, error_code); LSQ_DEBUG("Got RST_STREAM; stream: %"PRIu64"; offset: 0x%"PRIX64, stream_id, offset); - if (LSQUIC_GQUIC_STREAM_HANDSHAKE == stream_id) + if (is_handshake_stream_id(mc, stream_id)) { LSQ_INFO("handshake stream reset, closing connection"); return 0; @@ -513,7 +530,7 @@ process_stream_frame (struct mini_conn *mc, lsquic_packet_in_t *packet_in, return 0; EV_LOG_STREAM_FRAME_IN(LSQUIC_LOG_CONN_ID, &stream_frame); LSQ_DEBUG("Got stream frame for stream #%"PRIu64, stream_frame.stream_id); - if (LSQUIC_GQUIC_STREAM_HANDSHAKE == stream_frame.stream_id) + if (is_handshake_stream_id(mc, stream_frame.stream_id)) { if (packet_in->pi_flags & PI_HSK_STREAM) { /* This is not supported for simplicity. The spec recommends @@ -545,6 +562,47 @@ process_stream_frame (struct mini_conn *mc, lsquic_packet_in_t *packet_in, } +static unsigned +process_crypto_frame (struct mini_conn *mc, struct lsquic_packet_in *packet_in, + const unsigned char *p, size_t len) +{ + stream_frame_t stream_frame; + int parsed_len; + parsed_len = mc->mc_conn.cn_pf->pf_parse_crypto_frame(p, len, + &stream_frame); + if (parsed_len < 0) + return 0; + EV_LOG_CRYPTO_FRAME_IN(LSQUIC_LOG_CONN_ID, &stream_frame, + lsquic_packet_in_enc_level(packet_in)); + LSQ_DEBUG("Got CRYPTO frame at encryption level %s", + lsquic_enclev2str[lsquic_packet_in_enc_level(packet_in)]); + if (packet_in->pi_flags & PI_HSK_STREAM) + { /* This is not supported for simplicity: assume a single CRYPTO frame + * per packet. If this changes, we can revisit this code. + */ + LSQ_INFO("two handshake stream frames in single incoming packet"); + MCHIST_APPEND(mc, MCHE_2HSK_1STREAM); + return 0; + } + if (stream_frame.data_frame.df_offset >= mc->mc_read_off) + { + packet_in->pi_flags |= PI_HSK_STREAM; + packet_in->pi_hsk_stream = p - packet_in->pi_data; + mc->mc_flags |= MC_HAVE_NEW_HSK; + MCHIST_APPEND(mc, MCHE_NEW_HSK); + if (0 == stream_frame.data_frame.df_offset) + /* First CHLO message: update maximum packet size */ + mc->mc_path.np_pack_size = packet_in->pi_data_sz; + } + else + { + LSQ_DEBUG("drop duplicate frame"); + MCHIST_APPEND(mc, MCHE_DUP_HSK); + } + return parsed_len; +} + + static unsigned process_window_update_frame (struct mini_conn *mc, lsquic_packet_in_t *packet_in, const unsigned char *p, size_t len) @@ -556,7 +614,7 @@ process_window_update_frame (struct mini_conn *mc, if (parsed_len < 0) return 0; EV_LOG_WINDOW_UPDATE_FRAME_IN(LSQUIC_LOG_CONN_ID, stream_id, offset); - if (LSQUIC_GQUIC_STREAM_HANDSHAKE == stream_id) + if (is_handshake_stream_id(mc, stream_id)) /* This should not happen: why would the client send us WINDOW_UPDATE * on stream 1? */ @@ -575,6 +633,7 @@ static process_frame_f const process_frames[N_QUIC_FRAMES] = [QUIC_FRAME_ACK] = process_ack_frame, [QUIC_FRAME_BLOCKED] = process_blocked_frame, [QUIC_FRAME_CONNECTION_CLOSE] = process_connection_close_frame, + [QUIC_FRAME_CRYPTO] = process_crypto_frame, [QUIC_FRAME_GOAWAY] = process_goaway_frame, [QUIC_FRAME_INVALID] = process_invalid_frame, [QUIC_FRAME_PADDING] = process_padding_frame, @@ -632,40 +691,58 @@ conn_decrypt_packet (struct mini_conn *conn, lsquic_packet_in_t *packet_in) } -static enum { PRP_KEEP, PRP_DEFER, PRP_DROP, PRP_ERROR } +/* PRP: Process Regular Packet */ +enum proc_rp { PRP_KEEP, PRP_DEFER, PRP_DROP, PRP_ERROR, }; + + +static enum proc_rp +conn_decrypt_packet_or (struct mini_conn *mc, + struct lsquic_packet_in *packet_in) +{ + if (DECPI_OK == conn_decrypt_packet(mc, packet_in)) + { + MCHIST_APPEND(mc, MCHE_DECRYPTED); + return PRP_KEEP; + } + else if (mc->mc_conn.cn_esf.g->esf_have_key_gt_one( + mc->mc_conn.cn_enc_session)) + { + LSQ_INFO("could not decrypt packet: drop"); + mc->mc_dropped_packnos |= MCONN_PACKET_MASK(packet_in->pi_packno); + MCHIST_APPEND(mc, MCHE_UNDECR_DROP); + return PRP_DROP; + } + else if ((packet_in->pi_flags & PI_OWN_DATA) || + 0 == lsquic_conn_copy_and_release_pi_data(&mc->mc_conn, + mc->mc_enpub, packet_in)) + { + assert(packet_in->pi_flags & PI_OWN_DATA); + LSQ_INFO("could not decrypt packet: defer"); + mc->mc_deferred_packnos |= MCONN_PACKET_MASK(packet_in->pi_packno); + MCHIST_APPEND(mc, MCHE_UNDECR_DEFER); + return PRP_DEFER; + } + else + { + MCHIST_APPEND(mc, MCHE_ENOMEM); + return PRP_ERROR; /* Memory allocation must have failed */ + } +} + + +static enum proc_rp process_regular_packet (struct mini_conn *mc, lsquic_packet_in_t *packet_in) { const unsigned char *p, *pend; + enum proc_rp prp; unsigned len; /* Decrypt packet if necessary */ if (0 == (packet_in->pi_flags & PI_DECRYPTED)) { - if (DECPI_OK == conn_decrypt_packet(mc, packet_in)) - MCHIST_APPEND(mc, MCHE_DECRYPTED); - else if (mc->mc_conn.cn_esf.g->esf_have_key_gt_one( - mc->mc_conn.cn_enc_session)) - { - LSQ_INFO("could not decrypt packet: drop"); - mc->mc_dropped_packnos |= MCONN_PACKET_MASK(packet_in->pi_packno); - MCHIST_APPEND(mc, MCHE_UNDECR_DROP); - return PRP_DROP; - } - else if ((packet_in->pi_flags & PI_OWN_DATA) || - 0 == lsquic_conn_copy_and_release_pi_data(&mc->mc_conn, - mc->mc_enpub, packet_in)) - { - assert(packet_in->pi_flags & PI_OWN_DATA); - LSQ_INFO("could not decrypt packet: defer"); - mc->mc_deferred_packnos |= MCONN_PACKET_MASK(packet_in->pi_packno); - MCHIST_APPEND(mc, MCHE_UNDECR_DEFER); - return PRP_DEFER; - } - else - { - MCHIST_APPEND(mc, MCHE_ENOMEM); - return PRP_ERROR; /* Memory allocation must have failed */ - } + prp = conn_decrypt_packet_or(mc, packet_in); + if (prp != PRP_KEEP) + return prp; } /* Update receive history before processing the packet: if there is an @@ -749,6 +826,18 @@ mini_stream_read (void *stream, void *buf, size_t len, int *reached_fin) } +/* Wrapper to throw out reached_fin */ +static size_t +mini_stream_read_for_crypto (void *stream, void *buf, size_t len) +{ + size_t retval; + int reached_fin; + + retval = mini_stream_read(stream, buf, len, &reached_fin); + return retval; +} + + static size_t mini_stream_size (void *stream) { @@ -811,6 +900,13 @@ allocate_packet_out (struct mini_conn *mc, const unsigned char *nonce) packet_out->po_flags |= PO_HELLO; packet_out->po_header_type = HETY_0RTT; } + if (mc->mc_conn.cn_version >= LSQVER_050) + { + if (nonce) + packet_out->po_header_type = HETY_0RTT; + else + packet_out->po_header_type = HETY_INITIAL; + } lsquic_packet_out_set_pns(packet_out, PNS_APP); TAILQ_INSERT_TAIL(&mc->mc_packets_out, packet_out, po_next); LSQ_DEBUG("allocated packet #%"PRIu64", nonce: %d", packno, !!nonce); @@ -820,14 +916,89 @@ allocate_packet_out (struct mini_conn *mc, const unsigned char *nonce) } +static struct lsquic_packet_out * +to_packet_pre_Q050 (struct mini_conn *mc, struct mini_stream_ctx *ms_ctx, + const unsigned char *nonce) +{ + struct lsquic_packet_out *packet_out; + size_t cur_off; + int len; + + packet_out = allocate_packet_out(mc, nonce); + if (!packet_out) + return NULL; + cur_off = ms_ctx->off; + len = mc->mc_conn.cn_pf->pf_gen_stream_frame( + packet_out->po_data + packet_out->po_data_sz, + lsquic_packet_out_avail(packet_out), + 1, mc->mc_write_off, mini_stream_fin(ms_ctx), + mini_stream_size(ms_ctx), mini_stream_read, ms_ctx); + if (len < 0) + { + LSQ_WARN("cannot generate STREAM frame (avail: %u)", + lsquic_packet_out_avail(packet_out)); + return NULL; + } + mc->mc_write_off += ms_ctx->off - cur_off; + EV_LOG_GENERATED_STREAM_FRAME(LSQUIC_LOG_CONN_ID, mc->mc_conn.cn_pf, + packet_out->po_data + packet_out->po_data_sz, len); + packet_out->po_data_sz += len; + packet_out->po_frame_types |= 1 << QUIC_FRAME_STREAM; + if (0 == lsquic_packet_out_avail(packet_out)) + packet_out->po_flags |= PO_STREAM_END; + + return packet_out; +} + + +static struct lsquic_packet_out * +to_packet_Q050plus (struct mini_conn *mc, struct mini_stream_ctx *ms_ctx, + const unsigned char *nonce) +{ + struct lsquic_packet_out *packet_out; + size_t cur_off; + int len; + + if (nonce && !(mc->mc_flags & MC_WR_OFF_RESET)) + { + mc->mc_write_off = 0; + mc->mc_flags |= MC_WR_OFF_RESET; + } + + packet_out = allocate_packet_out(mc, nonce); + if (!packet_out) + return NULL; + cur_off = ms_ctx->off; + len = mc->mc_conn.cn_pf->pf_gen_crypto_frame( + packet_out->po_data + packet_out->po_data_sz, + lsquic_packet_out_avail(packet_out), mc->mc_write_off, + mini_stream_size(ms_ctx), mini_stream_read_for_crypto, ms_ctx); + if (len < 0) + { + LSQ_WARN("cannot generate CRYPTO frame (avail: %u)", + lsquic_packet_out_avail(packet_out)); + return NULL; + } + mc->mc_write_off += ms_ctx->off - cur_off; + EV_LOG_GENERATED_CRYPTO_FRAME(LSQUIC_LOG_CONN_ID, mc->mc_conn.cn_pf, + packet_out->po_data + packet_out->po_data_sz, len); + packet_out->po_data_sz += len; + packet_out->po_frame_types |= 1 << QUIC_FRAME_CRYPTO; + + return packet_out; +} + + static int packetize_response (struct mini_conn *mc, const unsigned char *buf, size_t bufsz, const unsigned char *nonce) { struct mini_stream_ctx ms_ctx; lsquic_packet_out_t *packet_out; - size_t cur_off; - int len; + struct lsquic_packet_out * (*const to_packet) (struct mini_conn *, + struct mini_stream_ctx *, const unsigned char *) + = mc->mc_conn.cn_version < LSQVER_050 + ? to_packet_pre_Q050 : to_packet_Q050plus; LSQ_DEBUG("Packetizing %zd bytes of handshake response", bufsz); @@ -837,27 +1008,9 @@ packetize_response (struct mini_conn *mc, const unsigned char *buf, do { - packet_out = allocate_packet_out(mc, nonce); + packet_out = to_packet(mc, &ms_ctx, nonce); if (!packet_out) return -1; - cur_off = ms_ctx.off; - len = mc->mc_conn.cn_pf->pf_gen_stream_frame(packet_out->po_data + packet_out->po_data_sz, - lsquic_packet_out_avail(packet_out), - LSQUIC_GQUIC_STREAM_HANDSHAKE, mc->mc_write_off, mini_stream_fin(&ms_ctx), - mini_stream_size(&ms_ctx), mini_stream_read, &ms_ctx); - if (len < 0) - { - LSQ_WARN("cannot generate stream frame (avail: %u)", - lsquic_packet_out_avail(packet_out)); - return -1; - } - mc->mc_write_off += ms_ctx.off - cur_off; - EV_LOG_GENERATED_STREAM_FRAME(LSQUIC_LOG_CONN_ID, mc->mc_conn.cn_pf, - packet_out->po_data + packet_out->po_data_sz, len); - packet_out->po_data_sz += len; - packet_out->po_frame_types |= 1 << QUIC_FRAME_STREAM; - if (0 == lsquic_packet_out_avail(packet_out)) - packet_out->po_flags |= PO_STREAM_END; } while (mini_stream_has_data(&ms_ctx)); @@ -893,6 +1046,10 @@ continue_handshake (struct mini_conn *mc) struct hsk_chunk hsk_chunks[MINICONN_MAX_PACKETS], *hsk_chunk; unsigned char nonce_buf[32]; int nonce_set = 0; + int (*parse_frame)(const unsigned char *, size_t, struct stream_frame *) + = mc->mc_conn.cn_version < LSQVER_050 + ? mc->mc_conn.cn_pf->pf_parse_stream_frame + : mc->mc_conn.cn_pf->pf_parse_crypto_frame; /* Get handshake stream data from each packet that contains a handshake * stream frame and place them into `hsk_chunks' array. @@ -902,7 +1059,7 @@ continue_handshake (struct mini_conn *mc) assert(n_hsk_chunks < sizeof(hsk_chunks) / sizeof(hsk_chunks[0])); if (0 == (packet_in->pi_flags & PI_HSK_STREAM)) continue; - s = mc->mc_conn.cn_pf->pf_parse_stream_frame(packet_in->pi_data + packet_in->pi_hsk_stream, + s = parse_frame(packet_in->pi_data + packet_in->pi_hsk_stream, packet_in->pi_data_sz - packet_in->pi_hsk_stream, &frame); if (-1 == s) { @@ -1485,7 +1642,14 @@ process_packet (struct mini_conn *mc, struct lsquic_packet_in *packet_in) case PRP_DEFER: assert(packet_in->pi_flags & PI_OWN_DATA); lsquic_packet_in_upref(packet_in); - TAILQ_INSERT_TAIL(&mc->mc_deferred, packet_in, pi_next); + if (mc->mc_n_deferred < MINI_CONN_MAX_DEFERRED) + { + TAILQ_INSERT_TAIL(&mc->mc_deferred, packet_in, pi_next); + ++mc->mc_n_deferred; + } + else + LSQ_DEBUG("won't defer more than %u packets: drop", + MINI_CONN_MAX_DEFERRED); break; case PRP_ERROR: mc->mc_flags |= MC_ERROR; @@ -1514,6 +1678,7 @@ insert_into_deferred (struct mini_conn *mc, lsquic_packet_in_t *new_packet) TAILQ_INSERT_BEFORE(packet_in, new_packet, pi_next); else TAILQ_INSERT_TAIL(&mc->mc_deferred, new_packet, pi_next); + ++mc->mc_n_deferred; } @@ -1528,6 +1693,7 @@ process_deferred_packets (struct mini_conn *mc) { packet_in = TAILQ_FIRST(&mc->mc_deferred); TAILQ_REMOVE(&mc->mc_deferred, packet_in, pi_next); + --mc->mc_n_deferred; process_packet(mc, packet_in); reached_last = packet_in == last; lsquic_packet_in_put(&mc->mc_enpub->enp_mm, packet_in); @@ -1662,11 +1828,75 @@ mini_conn_ci_packet_in (struct lsquic_conn *lconn, if (TAILQ_EMPTY(&mc->mc_deferred)) process_packet(mc, packet_in); - else + else if (mc->mc_n_deferred < MINI_CONN_MAX_DEFERRED) { insert_into_deferred(mc, packet_in); process_deferred_packets(mc); } + else + LSQ_DEBUG("won't defer more than %u packets: drop", + MINI_CONN_MAX_DEFERRED); +} + + +/* Q050 is different is that packet numbers are not known until after the + * packet is decrypted, so we have to follow different logic here. + */ +static void +mini_conn_ci_Q050_packet_in (struct lsquic_conn *lconn, + struct lsquic_packet_in *packet_in) +{ + struct mini_conn *mc = (struct mini_conn *) lconn; + enum proc_rp prp; + + if (mc->mc_flags & MC_ERROR) + { + LSQ_DEBUG("error state: ignore packet"); + return; + } + + if (!mc->mc_conn.cn_enc_session) + { + mc->mc_conn.cn_enc_session = + mc->mc_conn.cn_esf.g->esf_create_server(&mc->mc_conn, + mc->mc_conn.cn_cid, mc->mc_enpub); + if (!mc->mc_conn.cn_enc_session) + { + LSQ_WARN("cannot create new enc session"); + mc->mc_flags |= MC_ERROR; + return; + } + MCHIST_APPEND(mc, MCHE_NEW_ENC_SESS); + } + + assert(!(packet_in->pi_flags & PI_DECRYPTED)); + prp = conn_decrypt_packet_or(mc, packet_in); + switch (prp) + { + case PRP_KEEP: + break; + case PRP_DROP: + return; + case PRP_ERROR: + mc->mc_flags |= MC_ERROR; + return; + default: + if (mc->mc_n_deferred >= MINI_CONN_MAX_DEFERRED) + { + LSQ_DEBUG("won't defer more than %u packets: drop", + MINI_CONN_MAX_DEFERRED); + return; + } + assert(prp == PRP_DEFER); + assert(packet_in->pi_flags & PI_OWN_DATA); + lsquic_packet_in_upref(packet_in); + TAILQ_INSERT_TAIL(&mc->mc_deferred, packet_in, pi_next); + ++mc->mc_n_deferred; + return; + } + + assert(prp == PRP_KEEP); + process_packet(mc, packet_in); } @@ -1735,6 +1965,7 @@ mini_conn_ci_destroy (struct lsquic_conn *lconn) while ((packet_in = TAILQ_FIRST(&mc->mc_deferred))) { TAILQ_REMOVE(&mc->mc_deferred, packet_in, pi_next); + --mc->mc_n_deferred; still_deferred |= MCONN_PACKET_MASK(packet_in->pi_packno); lsquic_packet_in_put(&mc->mc_enpub->enp_mm, packet_in); } @@ -2012,6 +2243,26 @@ static const struct conn_iface mini_conn_iface_standard = { }; +static const struct conn_iface mini_conn_iface_standard_Q050 = { + .ci_abort_error = mini_conn_ci_abort_error, + .ci_client_call_on_new = mini_conn_ci_client_call_on_new, + .ci_destroy = mini_conn_ci_destroy, + .ci_get_engine = mini_conn_ci_get_engine, + .ci_get_path = mini_conn_ci_get_path, + .ci_hsk_done = mini_conn_ci_hsk_done, + .ci_internal_error = mini_conn_ci_internal_error, + .ci_is_tickable = mini_conn_ci_is_tickable, + .ci_next_packet_to_send = mini_conn_ci_next_packet_to_send, + .ci_next_tick_time = mini_conn_ci_next_tick_time, + .ci_packet_in = mini_conn_ci_Q050_packet_in, + .ci_packet_not_sent = mini_conn_ci_packet_not_sent, + .ci_packet_sent = mini_conn_ci_packet_sent, + .ci_record_addrs = mini_conn_ci_record_addrs, + .ci_tick = mini_conn_ci_tick, + .ci_tls_alert = mini_conn_ci_tls_alert, +}; + + #if LSQUIC_ENABLE_HANDSHAKE_DISABLE static const struct conn_iface mini_conn_iface_disable_handshake = { .ci_abort_error = mini_conn_ci_abort_error, diff --git a/src/liblsquic/lsquic_mini_conn.h b/src/liblsquic/lsquic_mini_conn.h index 88c34bb..21567c0 100644 --- a/src/liblsquic/lsquic_mini_conn.h +++ b/src/liblsquic/lsquic_mini_conn.h @@ -116,6 +116,8 @@ struct mini_conn { mc_cutoff, mc_cur_packno; unsigned char mc_hsk_count; +#define MINI_CONN_MAX_DEFERRED 10 + unsigned char mc_n_deferred; #if LSQUIC_RECORD_INORD_HIST unsigned char mc_inord_idx; #endif @@ -131,7 +133,7 @@ struct mini_conn { MC_HAVE_NEW_HSK = (1 << 0), MC_PROMOTE = (1 << 1), MC_HAVE_SHLO = (1 << 2), - MC_UNUSED_3 = (1 << 3), + MC_WR_OFF_RESET = (1 << 3), MC_ERROR = (1 << 4), MC_UNSENT_ACK = (1 << 5), MC_GEN_ACK = (1 << 6), diff --git a/src/liblsquic/lsquic_mini_conn_ietf.c b/src/liblsquic/lsquic_mini_conn_ietf.c index 03d4a39..2a1dc31 100644 --- a/src/liblsquic/lsquic_mini_conn_ietf.c +++ b/src/liblsquic/lsquic_mini_conn_ietf.c @@ -185,7 +185,7 @@ imico_stream_write (void *stream, const void *bufp, size_t bufsz) while (msg_ctx.buf < msg_ctx.end) { header_sz = lconn->cn_pf->pf_calc_crypto_frame_header_sz( - cryst->mcs_write_off); + cryst->mcs_write_off, msg_ctx.end - msg_ctx.buf); need = header_sz + 1; packet_out = imico_get_packet_out(conn, el2hety[ cryst->mcs_enc_level ], need); diff --git a/src/liblsquic/lsquic_packet_common.h b/src/liblsquic/lsquic_packet_common.h index 1fbb421..b95585d 100644 --- a/src/liblsquic/lsquic_packet_common.h +++ b/src/liblsquic/lsquic_packet_common.h @@ -30,7 +30,7 @@ enum quic_frame_type QUIC_FRAME_STOP_SENDING, /* I */ QUIC_FRAME_PATH_CHALLENGE, /* I */ QUIC_FRAME_PATH_RESPONSE, /* I */ - QUIC_FRAME_CRYPTO, /* I */ + QUIC_FRAME_CRYPTO, /* B */ QUIC_FRAME_RETIRE_CONNECTION_ID,/* I */ QUIC_FRAME_NEW_TOKEN, /* I */ N_QUIC_FRAMES diff --git a/src/liblsquic/lsquic_packet_out.c b/src/liblsquic/lsquic_packet_out.c index 02efa0a..4dac8ea 100644 --- a/src/liblsquic/lsquic_packet_out.c +++ b/src/liblsquic/lsquic_packet_out.c @@ -241,7 +241,17 @@ lsquic_packet_out_new (struct lsquic_mm *mm, struct malo *malo, int use_cid, memcpy(packet_out->po_nonce, nonce, 32); } if (flags & PO_LONGHEAD) - packet_out->po_header_type = HETY_HANDSHAKE; + { + if (lconn->cn_version == LSQVER_050) + { + if (lconn->cn_flags & (LSCONN_SERVER|LSCONN_HANDSHAKE_DONE)) + packet_out->po_header_type = HETY_0RTT; + else + packet_out->po_header_type = HETY_INITIAL; + } + else + packet_out->po_header_type = HETY_HANDSHAKE; + } packet_out->po_path = path; return packet_out; diff --git a/src/liblsquic/lsquic_packet_out.h b/src/liblsquic/lsquic_packet_out.h index 6b11e12..81bea18 100644 --- a/src/liblsquic/lsquic_packet_out.h +++ b/src/liblsquic/lsquic_packet_out.h @@ -99,7 +99,7 @@ typedef struct lsquic_packet_out */ PO_SCHED = (1 <<14), /* On scheduled queue */ PO_SENT_SZ = (1 <<15), - PO_LONGHEAD = (1 <<16), /* Only used for Q044 */ + PO_LONGHEAD = (1 <<16), #define POIPv6_SHIFT 20 PO_IPv6 = (1 <<20), /* Set if pmi_allocate was passed is_ipv6=1, * otherwise unset. diff --git a/src/liblsquic/lsquic_parse.h b/src/liblsquic/lsquic_parse.h index 1f22dfa..bf1662e 100644 --- a/src/liblsquic/lsquic_parse.h +++ b/src/liblsquic/lsquic_parse.h @@ -212,7 +212,7 @@ struct parse_funcs (*pf_calc_stream_frame_header_sz) (lsquic_stream_id_t stream_id, uint64_t offset, unsigned data_sz); size_t - (*pf_calc_crypto_frame_header_sz) (uint64_t offset); + (*pf_calc_crypto_frame_header_sz) (uint64_t offset, unsigned data_sz); void (*pf_turn_on_fin) (unsigned char *); @@ -234,7 +234,7 @@ struct parse_funcs unsigned (*pf_packno_bits2len) (enum packno_bits); - /* Only used by IETF QUIC: */ + /* Used by IETF QUIC and gQUIC >= Q050 */ void (*pf_packno_info) (const struct lsquic_conn *, const struct lsquic_packet_out *, unsigned *packno_off, @@ -318,13 +318,16 @@ struct parse_funcs extern const struct parse_funcs lsquic_parse_funcs_gquic_Q039; extern const struct parse_funcs lsquic_parse_funcs_gquic_Q046; +extern const struct parse_funcs lsquic_parse_funcs_gquic_Q050; extern const struct parse_funcs lsquic_parse_funcs_ietf_v1; #define select_pf_by_ver(ver) ( \ (1 << (ver)) & ((1 << LSQVER_039)|(1 << LSQVER_043)) ? \ &lsquic_parse_funcs_gquic_Q039 : \ - (1 << (ver)) & ((1 << LSQVER_046)|LSQUIC_EXPERIMENTAL_Q098) ? \ + (1 << (ver)) & (1 << LSQVER_046) ? \ &lsquic_parse_funcs_gquic_Q046 : \ + (1 << (ver)) & ((1 << LSQVER_050)|LSQUIC_EXPERIMENTAL_Q098) ? \ + &lsquic_parse_funcs_gquic_Q050 : \ &lsquic_parse_funcs_ietf_v1) /* This function is gQUIC-version independent */ @@ -340,8 +343,12 @@ int lsquic_Q046_parse_packet_in_long_begin (struct lsquic_packet_in *, size_t length, int is_server, unsigned, struct packin_parse_state *); +int +lsquic_Q050_parse_packet_in_long_begin (struct lsquic_packet_in *, size_t length, + int is_server, unsigned, struct packin_parse_state *); + enum quic_frame_type -parse_frame_type_gquic_Q035_thru_Q039 (unsigned char first_byte); +lsquic_parse_frame_type_gquic_Q035_thru_Q046 (unsigned char first_byte); extern const enum quic_frame_type lsquic_iquic_byte2type[0x100]; @@ -391,7 +398,7 @@ char * acki2str (const struct ack_info *acki, size_t *sz); void -lsquic_turn_on_fin_Q035_thru_Q039 (unsigned char *); +lsquic_turn_on_fin_Q035_thru_Q046 (unsigned char *); enum packno_bits lsquic_gquic_calc_packno_bits (lsquic_packno_t packno, diff --git a/src/liblsquic/lsquic_parse_Q046.c b/src/liblsquic/lsquic_parse_Q046.c index deb3095..730f3d1 100644 --- a/src/liblsquic/lsquic_parse_Q046.c +++ b/src/liblsquic/lsquic_parse_Q046.c @@ -262,6 +262,33 @@ gquic_Q046_parse_packet_in_finish (struct lsquic_packet_in *packet_in, } +static int +gquic_Q046_gen_crypto_frame (unsigned char *buf, size_t buf_len, + uint64_t offset, size_t size, gcf_read_f gcf_read, void *stream) +{ + assert(0); + return -1; +} + + +static int +gquic_Q046_parse_crypto_frame (const unsigned char *buf, size_t rem_packet_sz, + struct stream_frame *stream_frame) +{ + assert(0); + return -1; +} + + +static void +gquic_Q046_packno_info (const struct lsquic_conn *lconn, + const struct lsquic_packet_out *packet_out, unsigned *packno_off, + unsigned *packno_len) +{ + assert(0); +} + + const struct parse_funcs lsquic_parse_funcs_gquic_Q046 = { .pf_gen_reg_pkt_header = gquic_Q046_gen_reg_pkt_header, @@ -290,10 +317,13 @@ const struct parse_funcs lsquic_parse_funcs_gquic_Q046 = .pf_read_float_time16 = gquic_be_read_float_time16, #endif .pf_generate_simple_prst = lsquic_generate_iquic_reset, - .pf_parse_frame_type = parse_frame_type_gquic_Q035_thru_Q039, - .pf_turn_on_fin = lsquic_turn_on_fin_Q035_thru_Q039, + .pf_parse_frame_type = lsquic_parse_frame_type_gquic_Q035_thru_Q046, + .pf_turn_on_fin = lsquic_turn_on_fin_Q035_thru_Q046, .pf_packout_size = gquic_Q046_packout_size, .pf_packout_max_header_size = gquic_Q046_packout_header_size, .pf_calc_packno_bits = gquic_Q046_calc_packno_bits, .pf_packno_bits2len = gquic_Q046_packno_bits2len, + .pf_gen_crypto_frame = gquic_Q046_gen_crypto_frame, + .pf_parse_crypto_frame = gquic_Q046_parse_crypto_frame, + .pf_packno_info = gquic_Q046_packno_info, }; diff --git a/src/liblsquic/lsquic_parse_Q050.c b/src/liblsquic/lsquic_parse_Q050.c new file mode 100644 index 0000000..674de5e --- /dev/null +++ b/src/liblsquic/lsquic_parse_Q050.c @@ -0,0 +1,866 @@ +/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */ +/* + * lsquic_parse_Q050.c -- Parsing functions specific to GQUIC Q050 + */ + +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#else +#include +#endif + +#include "lsquic_types.h" +#include "lsquic_int_types.h" +#include "lsquic_packet_common.h" +#include "lsquic_packet_in.h" +#include "lsquic_packet_out.h" +#include "lsquic_parse.h" +#include "lsquic_parse_common.h" +#include "lsquic_version.h" +#include "lsquic.h" +#include "lsquic_parse_gquic_be.h" +#include "lsquic_parse_ietf.h" +#include "lsquic_byteswap.h" +#include "lsquic_hash.h" +#include "lsquic_conn.h" +#include "lsquic_varint.h" +#include "lsquic_enc_sess.h" + +#define LSQUIC_LOGGER_MODULE LSQLM_PARSE +#include "lsquic_logger.h" + + +/* [draft-ietf-quic-transport-24] Section-17.2 */ +static const enum header_type bits2ht[4] = +{ + [0] = HETY_INITIAL, + [1] = HETY_0RTT, + [2] = HETY_HANDSHAKE, + [3] = HETY_RETRY, +}; + + +int +lsquic_Q050_parse_packet_in_long_begin (struct lsquic_packet_in *packet_in, + size_t length, int is_server, unsigned cid_len, + struct packin_parse_state *state) +{ + const unsigned char *p = packet_in->pi_data; + const unsigned char *const end = p + length; + lsquic_ver_tag_t tag; + enum header_type header_type; + unsigned dcil, scil, odcil; + int verneg, r; + unsigned char first_byte; + uint64_t payload_len, token_len; + + if (length < 6) + return -1; + first_byte = *p++; + + memcpy(&tag, p, 4); + p += 4; + verneg = 0 == tag; + if (!verneg) + header_type = bits2ht[ (first_byte >> 4) & 3 ]; + else + header_type = HETY_VERNEG; + + packet_in->pi_header_type = header_type; + + dcil = *p++; + if (p + dcil >= end || dcil > MAX_CID_LEN) + return -1; + if (dcil) + { + memcpy(packet_in->pi_dcid.idbuf, p, dcil); + packet_in->pi_flags |= PI_CONN_ID; + p += dcil; + packet_in->pi_dcid.len = dcil; + } + + scil = *p++; + if (p + scil > end || scil > MAX_CID_LEN) + return -1; + if (scil) + { + memcpy(packet_in->pi_dcid.idbuf, p, scil); + packet_in->pi_flags |= PI_CONN_ID; + p += scil; + packet_in->pi_dcid.len = scil; + } + + if (is_server) + { + if (scil) + return -1; + } + else + if (dcil) + return -1; + + switch (header_type) + { + case HETY_INITIAL: + r = vint_read(p, end, &token_len); + if (r < 0) + return -1; + if (token_len && !is_server) + { + /* From [draft-ietf-quic-transport-14]: + * + * Token Length: A variable-length integer specifying the + * length of the Token field, in bytes. This value is zero + * if no token is present. Initial packets sent by the + * server MUST set the Token Length field to zero; clients + * that receive an Initial packet with a non-zero Token + * Length field MUST either discard the packet or generate + * a connection error of type PROTOCOL_VIOLATION. + */ + return -1; + } + p += r; + if (token_len) + { + if (token_len >= + 1ull << (sizeof(packet_in->pi_token_size) * 8)) + return -1; + if (p + token_len > end) + return -1; + packet_in->pi_token = p - packet_in->pi_data; + packet_in->pi_token_size = token_len; + p += token_len; + } + /* fall-through */ + case HETY_HANDSHAKE: + case HETY_0RTT: + if (p >= end) + return -1; + r = vint_read(p, end, &payload_len); + if (r < 0) + return -1; + p += r; + if (p - packet_in->pi_data + payload_len > length) + return -1; + length = p - packet_in->pi_data + payload_len; + if (end - p < 4) + return -1; + state->pps_p = p - r; + state->pps_nbytes = r; + packet_in->pi_quic_ver = 1; + break; + case HETY_RETRY: + if (p >= end) + return -1; + odcil = *p++; + if (p + odcil > end || odcil > MAX_CID_LEN) + return -1; + packet_in->pi_odcid_len = odcil; + packet_in->pi_odcid = p - packet_in->pi_data; + p += odcil; + packet_in->pi_token = p - packet_in->pi_data; + packet_in->pi_token_size = end - p; + p = end; + length = end - packet_in->pi_data; + state->pps_p = NULL; + state->pps_nbytes = 0; + packet_in->pi_quic_ver = 1; + break; + default: + assert(header_type == HETY_VERNEG); + if (p >= end || (3 & (uintptr_t) (end - p))) + return -1; + packet_in->pi_quic_ver = p - packet_in->pi_data; + p = end; + state->pps_p = NULL; + state->pps_nbytes = 0; + break; + } + + packet_in->pi_header_sz = p - packet_in->pi_data; + packet_in->pi_data_sz = length; + packet_in->pi_nonce = 0; + packet_in->pi_refcnt = 0; + packet_in->pi_frame_types = 0; + memset(&packet_in->pi_next, 0, sizeof(packet_in->pi_next)); + packet_in->pi_refcnt = 0; + packet_in->pi_received = 0; + + /* Packet number is set to an invalid value. The packet number must + * be decrypted, which happens later. + */ + packet_in->pi_packno = 1ULL << 62; + + return 0; +} + + + + +static unsigned +gquic_Q050_packno_bits2len (enum packno_bits bits) +{ + return bits + 1; +} + +#define iquic_packno_bits2len gquic_Q050_packno_bits2len + + +static enum packno_bits +gquic_Q050_calc_packno_bits (lsquic_packno_t packno, + lsquic_packno_t least_unacked, uint64_t n_in_flight) +{ + uint64_t delta; + unsigned bits; + + delta = packno - least_unacked; + if (n_in_flight > delta) + delta = n_in_flight; + + delta *= 4; + bits = (delta >= (1ULL << 8)) + + (delta >= (1ULL << 16)) + + (delta >= (1ULL << 24)) + ; + + return bits; +} + + +static unsigned +write_packno (unsigned char *p, lsquic_packno_t packno, enum packno_bits bits) +{ + unsigned char *const begin = p; + + switch (bits) + { + case IQUIC_PACKNO_LEN_4: + *p++ = packno >> 24; + /* fall-through */ + case IQUIC_PACKNO_LEN_3: + *p++ = packno >> 16; + /* fall-through */ + case IQUIC_PACKNO_LEN_2: + *p++ = packno >> 8; + /* fall-through */ + default: + *p++ = packno; + } + + return p - begin; +} + + +static int +gen_short_pkt_header (const struct lsquic_conn *lconn, + const struct lsquic_packet_out *packet_out, unsigned char *buf, + size_t bufsz) +{ + unsigned packno_len, need; + enum packno_bits bits; + + bits = lsquic_packet_out_packno_bits(packet_out); + packno_len = iquic_packno_bits2len(bits); + + if (lconn->cn_flags & LSCONN_SERVER) + need = 1 + packno_len; + else + need = 1 + 8 /* CID */ + packno_len; + + if (need > bufsz) + return -1; + + *buf++ = 0x40 | bits; + + if (0 == (lconn->cn_flags & LSCONN_SERVER)) + { + memcpy(buf, lconn->cn_cid.idbuf, 8); + buf += 8; + } + + (void) write_packno(buf, packet_out->po_packno, bits); + + return need; +} + + +static size_t +gquic_Q050_packout_header_size_long_by_flags (const struct lsquic_conn *lconn, + enum packet_out_flags flags) +{ + size_t sz; + enum packno_bits packno_bits; + + packno_bits = (flags >> POBIT_SHIFT) & 0x3; + + sz = 1 /* Type */ + + 4 /* Version */ + + 1 /* DCIL */ + + 1 /* SCIL */ + + lconn->cn_cid.len + + 1 /* Token length: only use for Initial packets, while token is never + * set in this version. + */ + + (flags & PO_NONCE ? DNONC_LENGTH : 0) + + 2 /* Always use two bytes to encode payload length */ + + iquic_packno_bits2len(packno_bits) + ; + + return sz; +} + + +/* [draft-ietf-quic-transport-17] Section-17.2 */ +static const unsigned char header_type_to_bin[] = { + [HETY_INITIAL] = 0x0, + [HETY_0RTT] = 0x1, + [HETY_HANDSHAKE] = 0x2, + [HETY_RETRY] = 0x3, +}; + + +static size_t +gquic_Q050_packout_header_size_long_by_packet (const struct lsquic_conn *lconn, + const struct lsquic_packet_out *packet_out) +{ + size_t sz; + enum packno_bits packno_bits; + + packno_bits = lsquic_packet_out_packno_bits(packet_out); + + sz = 1 /* Type */ + + 4 /* Version */ + + 1 /* DCIL */ + + 1 /* SCIL */ + + lconn->cn_cid.len + /* Token is never sent, but token length byte is used */ + + (packet_out->po_header_type == HETY_INITIAL) + + 2 /* Always use two bytes to encode payload length */ + + iquic_packno_bits2len(packno_bits) + + (packet_out->po_nonce ? DNONC_LENGTH : 0) + ; + + return sz; +} + + +static void +gquic_Q050_packno_info (const struct lsquic_conn *lconn, + const struct lsquic_packet_out *packet_out, unsigned *packno_off, + unsigned *packno_len) +{ + unsigned token_len; /* Need intermediate value to quiet compiler warning */ + + if (packet_out->po_header_type == HETY_NOT_SET) + *packno_off = 1 + + (lconn->cn_flags & LSCONN_SERVER ? 0 : 8); + else + *packno_off = 1 + + 4 + + 1 + + 1 + + lconn->cn_cid.len + + (packet_out->po_header_type == HETY_INITIAL ? + (token_len = packet_out->po_token_len, + (1 << vint_val2bits(token_len)) + token_len) : 0) + + 2; + *packno_len = iquic_packno_bits2len( + lsquic_packet_out_packno_bits(packet_out)); +} + + +static int +gquic_Q050_gen_long_pkt_header (const struct lsquic_conn *lconn, + const struct lsquic_packet_out *packet_out, unsigned char *buf, + size_t bufsz) +{ + enum packno_bits packno_bits; + lsquic_ver_tag_t ver_tag; + unsigned token_len, payload_len, bits; + unsigned char *p; + size_t need; + + need = gquic_Q050_packout_header_size_long_by_packet(lconn, packet_out); + if (need > bufsz) + { + errno = EINVAL; + return -1; + } + + packno_bits = lsquic_packet_out_packno_bits(packet_out); + p = buf; + *p++ = 0x80 | 0x40 + | (header_type_to_bin[ packet_out->po_header_type ] << 4) + | packno_bits; + ver_tag = lsquic_ver2tag(lconn->cn_version); + memcpy(p, &ver_tag, sizeof(ver_tag)); + p += sizeof(ver_tag); + + if (lconn->cn_flags & LSCONN_SERVER) + { + *p++ = 0; + *p++ = lconn->cn_cid.len; + memcpy(p, lconn->cn_cid.idbuf, lconn->cn_cid.len); + p += lconn->cn_cid.len; + } + else + { + *p++ = lconn->cn_cid.len; + memcpy(p, lconn->cn_cid.idbuf, lconn->cn_cid.len); + p += lconn->cn_cid.len; + *p++ = 0; + } + + if (HETY_INITIAL == packet_out->po_header_type) + { + token_len = packet_out->po_token_len; + bits = vint_val2bits(token_len); + vint_write(p, token_len, bits, 1 << bits); + p += 1 << bits; + memcpy(p, packet_out->po_token, token_len); + p += token_len; + } + + payload_len = packet_out->po_data_sz + + lconn->cn_esf_c->esf_tag_len + + iquic_packno_bits2len(packno_bits); + if (packet_out->po_nonce) + payload_len += DNONC_LENGTH; + bits = 1; /* Always use two bytes to encode payload length */ + vint_write(p, payload_len, bits, 1 << bits); + p += 1 << bits; + p += write_packno(p, packet_out->po_packno, packno_bits); + + if (packet_out->po_nonce) + { + memcpy(p, packet_out->po_nonce, DNONC_LENGTH); + p += DNONC_LENGTH; + } + + assert(need == (size_t) (p - buf)); + return p - buf; +} + + +static int +gquic_Q050_gen_reg_pkt_header (const struct lsquic_conn *lconn, + const struct lsquic_packet_out *packet_out, unsigned char *buf, + size_t bufsz) +{ + if (0 == (packet_out->po_flags & PO_LONGHEAD)) + return gen_short_pkt_header(lconn, packet_out, buf, bufsz); + else + return gquic_Q050_gen_long_pkt_header(lconn, packet_out, buf, bufsz); +} + + +static size_t +gquic_Q050_packout_header_size_short (const struct lsquic_conn *lconn, + enum packet_out_flags flags) +{ + enum packno_bits bits; + size_t sz; + + bits = (flags >> POBIT_SHIFT) & 0x3; + sz = 1; /* Type */ + sz += (lconn->cn_flags & LSCONN_SERVER) ? 0 : 8; + sz += iquic_packno_bits2len(bits); + + return sz; +} + + +static size_t +gquic_Q050_packout_max_header_size (const struct lsquic_conn *lconn, + enum packet_out_flags flags, size_t dcid_len_unused) +{ + if (lconn->cn_flags & LSCONN_SERVER) + { + if (0 == (flags & PO_LONGHEAD)) + return gquic_Q050_packout_header_size_short(lconn, flags); + else + return gquic_Q050_packout_header_size_long_by_flags(lconn, flags); + } + else + { + if (lconn->cn_flags & LSCONN_HANDSHAKE_DONE) + return gquic_Q050_packout_header_size_short(lconn, flags); + else + return gquic_Q050_packout_header_size_long_by_flags(lconn, flags); + } +} + + +static size_t +gquic_Q050_packout_size (const struct lsquic_conn *lconn, + const struct lsquic_packet_out *packet_out) +{ + size_t sz; + + if ((lconn->cn_flags & LSCONN_HANDSHAKE_DONE) + && packet_out->po_header_type == HETY_NOT_SET) + sz = gquic_Q050_packout_header_size_short(lconn, packet_out->po_flags); + else + sz = gquic_Q050_packout_header_size_long_by_packet(lconn, packet_out); + + sz += packet_out->po_data_sz; + sz += lconn->cn_esf_c->esf_tag_len; + + return sz; +} + + +void +gquic_Q050_parse_packet_in_finish (struct lsquic_packet_in *packet_in, + struct packin_parse_state *state) +{ +} + + +/* Same as Q046 plus CRYPTO frame at slot 8 */ +static const enum quic_frame_type byte2frame_type_Q050[0x100] = +{ + [0x00] = QUIC_FRAME_PADDING, + [0x01] = QUIC_FRAME_RST_STREAM, + [0x02] = QUIC_FRAME_CONNECTION_CLOSE, + [0x03] = QUIC_FRAME_GOAWAY, + [0x04] = QUIC_FRAME_WINDOW_UPDATE, + [0x05] = QUIC_FRAME_BLOCKED, + [0x06] = QUIC_FRAME_STOP_WAITING, + [0x07] = QUIC_FRAME_PING, + [0x08] = QUIC_FRAME_CRYPTO, + [0x09] = QUIC_FRAME_INVALID, + [0x0A] = QUIC_FRAME_INVALID, + [0x0B] = QUIC_FRAME_INVALID, + [0x0C] = QUIC_FRAME_INVALID, + [0x0D] = QUIC_FRAME_INVALID, + [0x0E] = QUIC_FRAME_INVALID, + [0x0F] = QUIC_FRAME_INVALID, + [0x10] = QUIC_FRAME_INVALID, + [0x11] = QUIC_FRAME_INVALID, + [0x12] = QUIC_FRAME_INVALID, + [0x13] = QUIC_FRAME_INVALID, + [0x14] = QUIC_FRAME_INVALID, + [0x15] = QUIC_FRAME_INVALID, + [0x16] = QUIC_FRAME_INVALID, + [0x17] = QUIC_FRAME_INVALID, + [0x18] = QUIC_FRAME_INVALID, + [0x19] = QUIC_FRAME_INVALID, + [0x1A] = QUIC_FRAME_INVALID, + [0x1B] = QUIC_FRAME_INVALID, + [0x1C] = QUIC_FRAME_INVALID, + [0x1D] = QUIC_FRAME_INVALID, + [0x1E] = QUIC_FRAME_INVALID, + [0x1F] = QUIC_FRAME_INVALID, + [0x20] = QUIC_FRAME_INVALID, + [0x21] = QUIC_FRAME_INVALID, + [0x22] = QUIC_FRAME_INVALID, + [0x23] = QUIC_FRAME_INVALID, + [0x24] = QUIC_FRAME_INVALID, + [0x25] = QUIC_FRAME_INVALID, + [0x26] = QUIC_FRAME_INVALID, + [0x27] = QUIC_FRAME_INVALID, + [0x28] = QUIC_FRAME_INVALID, + [0x29] = QUIC_FRAME_INVALID, + [0x2A] = QUIC_FRAME_INVALID, + [0x2B] = QUIC_FRAME_INVALID, + [0x2C] = QUIC_FRAME_INVALID, + [0x2D] = QUIC_FRAME_INVALID, + [0x2E] = QUIC_FRAME_INVALID, + [0x2F] = QUIC_FRAME_INVALID, + [0x30] = QUIC_FRAME_INVALID, + [0x31] = QUIC_FRAME_INVALID, + [0x32] = QUIC_FRAME_INVALID, + [0x33] = QUIC_FRAME_INVALID, + [0x34] = QUIC_FRAME_INVALID, + [0x35] = QUIC_FRAME_INVALID, + [0x36] = QUIC_FRAME_INVALID, + [0x37] = QUIC_FRAME_INVALID, + [0x38] = QUIC_FRAME_INVALID, + [0x39] = QUIC_FRAME_INVALID, + [0x3A] = QUIC_FRAME_INVALID, + [0x3B] = QUIC_FRAME_INVALID, + [0x3C] = QUIC_FRAME_INVALID, + [0x3D] = QUIC_FRAME_INVALID, + [0x3E] = QUIC_FRAME_INVALID, + [0x3F] = QUIC_FRAME_INVALID, + [0x40] = QUIC_FRAME_ACK, + [0x41] = QUIC_FRAME_ACK, + [0x42] = QUIC_FRAME_ACK, + [0x43] = QUIC_FRAME_ACK, + [0x44] = QUIC_FRAME_ACK, + [0x45] = QUIC_FRAME_ACK, + [0x46] = QUIC_FRAME_ACK, + [0x47] = QUIC_FRAME_ACK, + [0x48] = QUIC_FRAME_ACK, + [0x49] = QUIC_FRAME_ACK, + [0x4A] = QUIC_FRAME_ACK, + [0x4B] = QUIC_FRAME_ACK, + [0x4C] = QUIC_FRAME_ACK, + [0x4D] = QUIC_FRAME_ACK, + [0x4E] = QUIC_FRAME_ACK, + [0x4F] = QUIC_FRAME_ACK, + [0x50] = QUIC_FRAME_ACK, + [0x51] = QUIC_FRAME_ACK, + [0x52] = QUIC_FRAME_ACK, + [0x53] = QUIC_FRAME_ACK, + [0x54] = QUIC_FRAME_ACK, + [0x55] = QUIC_FRAME_ACK, + [0x56] = QUIC_FRAME_ACK, + [0x57] = QUIC_FRAME_ACK, + [0x58] = QUIC_FRAME_ACK, + [0x59] = QUIC_FRAME_ACK, + [0x5A] = QUIC_FRAME_ACK, + [0x5B] = QUIC_FRAME_ACK, + [0x5C] = QUIC_FRAME_ACK, + [0x5D] = QUIC_FRAME_ACK, + [0x5E] = QUIC_FRAME_ACK, + [0x5F] = QUIC_FRAME_ACK, + [0x60] = QUIC_FRAME_ACK, + [0x61] = QUIC_FRAME_ACK, + [0x62] = QUIC_FRAME_ACK, + [0x63] = QUIC_FRAME_ACK, + [0x64] = QUIC_FRAME_ACK, + [0x65] = QUIC_FRAME_ACK, + [0x66] = QUIC_FRAME_ACK, + [0x67] = QUIC_FRAME_ACK, + [0x68] = QUIC_FRAME_ACK, + [0x69] = QUIC_FRAME_ACK, + [0x6A] = QUIC_FRAME_ACK, + [0x6B] = QUIC_FRAME_ACK, + [0x6C] = QUIC_FRAME_ACK, + [0x6D] = QUIC_FRAME_ACK, + [0x6E] = QUIC_FRAME_ACK, + [0x6F] = QUIC_FRAME_ACK, + [0x70] = QUIC_FRAME_ACK, + [0x71] = QUIC_FRAME_ACK, + [0x72] = QUIC_FRAME_ACK, + [0x73] = QUIC_FRAME_ACK, + [0x74] = QUIC_FRAME_ACK, + [0x75] = QUIC_FRAME_ACK, + [0x76] = QUIC_FRAME_ACK, + [0x77] = QUIC_FRAME_ACK, + [0x78] = QUIC_FRAME_ACK, + [0x79] = QUIC_FRAME_ACK, + [0x7A] = QUIC_FRAME_ACK, + [0x7B] = QUIC_FRAME_ACK, + [0x7C] = QUIC_FRAME_ACK, + [0x7D] = QUIC_FRAME_ACK, + [0x7E] = QUIC_FRAME_ACK, + [0x7F] = QUIC_FRAME_ACK, + [0x80] = QUIC_FRAME_STREAM, + [0x81] = QUIC_FRAME_STREAM, + [0x82] = QUIC_FRAME_STREAM, + [0x83] = QUIC_FRAME_STREAM, + [0x84] = QUIC_FRAME_STREAM, + [0x85] = QUIC_FRAME_STREAM, + [0x86] = QUIC_FRAME_STREAM, + [0x87] = QUIC_FRAME_STREAM, + [0x88] = QUIC_FRAME_STREAM, + [0x89] = QUIC_FRAME_STREAM, + [0x8A] = QUIC_FRAME_STREAM, + [0x8B] = QUIC_FRAME_STREAM, + [0x8C] = QUIC_FRAME_STREAM, + [0x8D] = QUIC_FRAME_STREAM, + [0x8E] = QUIC_FRAME_STREAM, + [0x8F] = QUIC_FRAME_STREAM, + [0x90] = QUIC_FRAME_STREAM, + [0x91] = QUIC_FRAME_STREAM, + [0x92] = QUIC_FRAME_STREAM, + [0x93] = QUIC_FRAME_STREAM, + [0x94] = QUIC_FRAME_STREAM, + [0x95] = QUIC_FRAME_STREAM, + [0x96] = QUIC_FRAME_STREAM, + [0x97] = QUIC_FRAME_STREAM, + [0x98] = QUIC_FRAME_STREAM, + [0x99] = QUIC_FRAME_STREAM, + [0x9A] = QUIC_FRAME_STREAM, + [0x9B] = QUIC_FRAME_STREAM, + [0x9C] = QUIC_FRAME_STREAM, + [0x9D] = QUIC_FRAME_STREAM, + [0x9E] = QUIC_FRAME_STREAM, + [0x9F] = QUIC_FRAME_STREAM, + [0xA0] = QUIC_FRAME_STREAM, + [0xA1] = QUIC_FRAME_STREAM, + [0xA2] = QUIC_FRAME_STREAM, + [0xA3] = QUIC_FRAME_STREAM, + [0xA4] = QUIC_FRAME_STREAM, + [0xA5] = QUIC_FRAME_STREAM, + [0xA6] = QUIC_FRAME_STREAM, + [0xA7] = QUIC_FRAME_STREAM, + [0xA8] = QUIC_FRAME_STREAM, + [0xA9] = QUIC_FRAME_STREAM, + [0xAA] = QUIC_FRAME_STREAM, + [0xAB] = QUIC_FRAME_STREAM, + [0xAC] = QUIC_FRAME_STREAM, + [0xAD] = QUIC_FRAME_STREAM, + [0xAE] = QUIC_FRAME_STREAM, + [0xAF] = QUIC_FRAME_STREAM, + [0xB0] = QUIC_FRAME_STREAM, + [0xB1] = QUIC_FRAME_STREAM, + [0xB2] = QUIC_FRAME_STREAM, + [0xB3] = QUIC_FRAME_STREAM, + [0xB4] = QUIC_FRAME_STREAM, + [0xB5] = QUIC_FRAME_STREAM, + [0xB6] = QUIC_FRAME_STREAM, + [0xB7] = QUIC_FRAME_STREAM, + [0xB8] = QUIC_FRAME_STREAM, + [0xB9] = QUIC_FRAME_STREAM, + [0xBA] = QUIC_FRAME_STREAM, + [0xBB] = QUIC_FRAME_STREAM, + [0xBC] = QUIC_FRAME_STREAM, + [0xBD] = QUIC_FRAME_STREAM, + [0xBE] = QUIC_FRAME_STREAM, + [0xBF] = QUIC_FRAME_STREAM, + [0xC0] = QUIC_FRAME_STREAM, + [0xC1] = QUIC_FRAME_STREAM, + [0xC2] = QUIC_FRAME_STREAM, + [0xC3] = QUIC_FRAME_STREAM, + [0xC4] = QUIC_FRAME_STREAM, + [0xC5] = QUIC_FRAME_STREAM, + [0xC6] = QUIC_FRAME_STREAM, + [0xC7] = QUIC_FRAME_STREAM, + [0xC8] = QUIC_FRAME_STREAM, + [0xC9] = QUIC_FRAME_STREAM, + [0xCA] = QUIC_FRAME_STREAM, + [0xCB] = QUIC_FRAME_STREAM, + [0xCC] = QUIC_FRAME_STREAM, + [0xCD] = QUIC_FRAME_STREAM, + [0xCE] = QUIC_FRAME_STREAM, + [0xCF] = QUIC_FRAME_STREAM, + [0xD0] = QUIC_FRAME_STREAM, + [0xD1] = QUIC_FRAME_STREAM, + [0xD2] = QUIC_FRAME_STREAM, + [0xD3] = QUIC_FRAME_STREAM, + [0xD4] = QUIC_FRAME_STREAM, + [0xD5] = QUIC_FRAME_STREAM, + [0xD6] = QUIC_FRAME_STREAM, + [0xD7] = QUIC_FRAME_STREAM, + [0xD8] = QUIC_FRAME_STREAM, + [0xD9] = QUIC_FRAME_STREAM, + [0xDA] = QUIC_FRAME_STREAM, + [0xDB] = QUIC_FRAME_STREAM, + [0xDC] = QUIC_FRAME_STREAM, + [0xDD] = QUIC_FRAME_STREAM, + [0xDE] = QUIC_FRAME_STREAM, + [0xDF] = QUIC_FRAME_STREAM, + [0xE0] = QUIC_FRAME_STREAM, + [0xE1] = QUIC_FRAME_STREAM, + [0xE2] = QUIC_FRAME_STREAM, + [0xE3] = QUIC_FRAME_STREAM, + [0xE4] = QUIC_FRAME_STREAM, + [0xE5] = QUIC_FRAME_STREAM, + [0xE6] = QUIC_FRAME_STREAM, + [0xE7] = QUIC_FRAME_STREAM, + [0xE8] = QUIC_FRAME_STREAM, + [0xE9] = QUIC_FRAME_STREAM, + [0xEA] = QUIC_FRAME_STREAM, + [0xEB] = QUIC_FRAME_STREAM, + [0xEC] = QUIC_FRAME_STREAM, + [0xED] = QUIC_FRAME_STREAM, + [0xEE] = QUIC_FRAME_STREAM, + [0xEF] = QUIC_FRAME_STREAM, + [0xF0] = QUIC_FRAME_STREAM, + [0xF1] = QUIC_FRAME_STREAM, + [0xF2] = QUIC_FRAME_STREAM, + [0xF3] = QUIC_FRAME_STREAM, + [0xF4] = QUIC_FRAME_STREAM, + [0xF5] = QUIC_FRAME_STREAM, + [0xF6] = QUIC_FRAME_STREAM, + [0xF7] = QUIC_FRAME_STREAM, + [0xF8] = QUIC_FRAME_STREAM, + [0xF9] = QUIC_FRAME_STREAM, + [0xFA] = QUIC_FRAME_STREAM, + [0xFB] = QUIC_FRAME_STREAM, + [0xFC] = QUIC_FRAME_STREAM, + [0xFD] = QUIC_FRAME_STREAM, + [0xFE] = QUIC_FRAME_STREAM, + [0xFF] = QUIC_FRAME_STREAM, +}; + + +static enum quic_frame_type +gquic_Q050_parse_frame_type (unsigned char b) +{ + return byte2frame_type_Q050[b]; +} + + +static int +gquic_Q050_gen_crypto_frame (unsigned char *buf, size_t buf_len, + uint64_t offset, size_t size, gcf_read_f gcf_read, void *stream) +{ + return lsquic_ietf_v1_gen_crypto_frame(buf, 0x8, buf_len, offset, + size, gcf_read, stream); +} + + +static int +gquic_Q050_parse_crypto_frame (const unsigned char *buf, size_t rem_packet_sz, + struct stream_frame *stream_frame) +{ + if (rem_packet_sz > 0) + { + assert(0x08 == buf[0]); + return lsquic_ietf_v1_parse_crypto_frame(buf, rem_packet_sz, + stream_frame); + } + else + return -1; +} + + +static size_t +gquic_Q050_calc_crypto_frame_header_sz (uint64_t offset, unsigned data_sz) +{ + return 1 /* Frame type */ + + (1 << vint_val2bits(offset)) + + (1 << vint_val2bits(data_sz)) + ; +} + + +const struct parse_funcs lsquic_parse_funcs_gquic_Q050 = +{ + .pf_gen_reg_pkt_header = gquic_Q050_gen_reg_pkt_header, + .pf_parse_packet_in_finish = gquic_Q050_parse_packet_in_finish, + .pf_gen_stream_frame = gquic_be_gen_stream_frame, + .pf_calc_stream_frame_header_sz = calc_stream_frame_header_sz_gquic, + .pf_parse_stream_frame = gquic_be_parse_stream_frame, + .pf_parse_ack_frame = gquic_be_parse_ack_frame, + .pf_gen_ack_frame = gquic_be_gen_ack_frame, + .pf_gen_stop_waiting_frame = gquic_be_gen_stop_waiting_frame, + .pf_parse_stop_waiting_frame = gquic_be_parse_stop_waiting_frame, + .pf_skip_stop_waiting_frame = gquic_be_skip_stop_waiting_frame, + .pf_gen_window_update_frame = gquic_be_gen_window_update_frame, + .pf_parse_window_update_frame = gquic_be_parse_window_update_frame, + .pf_gen_blocked_frame = gquic_be_gen_blocked_frame, + .pf_parse_blocked_frame = gquic_be_parse_blocked_frame, + .pf_gen_rst_frame = gquic_be_gen_rst_frame, + .pf_parse_rst_frame = gquic_be_parse_rst_frame, + .pf_gen_connect_close_frame = gquic_be_gen_connect_close_frame, + .pf_parse_connect_close_frame = gquic_be_parse_connect_close_frame, + .pf_gen_goaway_frame = gquic_be_gen_goaway_frame, + .pf_parse_goaway_frame = gquic_be_parse_goaway_frame, + .pf_gen_ping_frame = gquic_be_gen_ping_frame, +#ifndef NDEBUG + .pf_write_float_time16 = gquic_be_write_float_time16, + .pf_read_float_time16 = gquic_be_read_float_time16, +#endif + .pf_generate_simple_prst = lsquic_generate_iquic_reset, + .pf_parse_frame_type = gquic_Q050_parse_frame_type, + .pf_turn_on_fin = lsquic_turn_on_fin_Q035_thru_Q046, + .pf_packout_size = gquic_Q050_packout_size, + .pf_packout_max_header_size = gquic_Q050_packout_max_header_size, + .pf_calc_packno_bits = gquic_Q050_calc_packno_bits, + .pf_packno_bits2len = gquic_Q050_packno_bits2len, + .pf_gen_crypto_frame = gquic_Q050_gen_crypto_frame, + .pf_parse_crypto_frame = gquic_Q050_parse_crypto_frame, + .pf_packno_info = gquic_Q050_packno_info, + .pf_calc_crypto_frame_header_sz = gquic_Q050_calc_crypto_frame_header_sz, +}; diff --git a/src/liblsquic/lsquic_parse_common.c b/src/liblsquic/lsquic_parse_common.c index d3a5151..68326b3 100644 --- a/src/liblsquic/lsquic_parse_common.c +++ b/src/liblsquic/lsquic_parse_common.c @@ -13,26 +13,31 @@ #include "lsquic_parse.h" #include "lsquic_enc_sess.h" #include "lsquic_version.h" +#include "lsquic_qtags.h" static int -parse_ietf_v1_or_Q046_long_begin (struct lsquic_packet_in *packet_in, +parse_ietf_v1_or_Q046plus_long_begin (struct lsquic_packet_in *packet_in, size_t length, int is_server, unsigned cid_len, struct packin_parse_state *state) { - enum lsquic_version version; lsquic_ver_tag_t tag; if (length >= 5) { memcpy(&tag, packet_in->pi_data + 1, 4); - version = lsquic_tag2ver(tag); - if (version == LSQVER_046) + switch (tag) + { + case TAG('Q', '0', '4', '6'): return lsquic_Q046_parse_packet_in_long_begin(packet_in, length, is_server, cid_len, state); - else + case TAG('Q', '0', '5', '0'): + return lsquic_Q050_parse_packet_in_long_begin(packet_in, length, + is_server, cid_len, state); + default: return lsquic_ietf_v1_parse_packet_in_long_begin(packet_in, length, is_server, cid_len, state); + } } else return -1; @@ -51,20 +56,20 @@ static int (* const parse_begin_funcs[32]) (struct lsquic_packet_in *, PBEL(0x80|0x40|0x20|0x10|0x00) = lsquic_Q046_parse_packet_in_long_begin, PBEL(0x80|0x00|0x20|0x10|0x00) = lsquic_Q046_parse_packet_in_long_begin, /* 1X00 XGGG: */ - PBEL(0x80|0x40|0x00|0x00|0x08) = parse_ietf_v1_or_Q046_long_begin, - PBEL(0x80|0x00|0x00|0x00|0x08) = parse_ietf_v1_or_Q046_long_begin, - PBEL(0x80|0x40|0x00|0x00|0x00) = parse_ietf_v1_or_Q046_long_begin, - PBEL(0x80|0x00|0x00|0x00|0x00) = parse_ietf_v1_or_Q046_long_begin, + PBEL(0x80|0x40|0x00|0x00|0x08) = parse_ietf_v1_or_Q046plus_long_begin, + PBEL(0x80|0x00|0x00|0x00|0x08) = parse_ietf_v1_or_Q046plus_long_begin, + PBEL(0x80|0x40|0x00|0x00|0x00) = parse_ietf_v1_or_Q046plus_long_begin, + PBEL(0x80|0x00|0x00|0x00|0x00) = parse_ietf_v1_or_Q046plus_long_begin, /* 1X01 XGGG: */ - PBEL(0x80|0x40|0x00|0x10|0x08) = parse_ietf_v1_or_Q046_long_begin, - PBEL(0x80|0x00|0x00|0x10|0x08) = parse_ietf_v1_or_Q046_long_begin, - PBEL(0x80|0x40|0x00|0x10|0x00) = parse_ietf_v1_or_Q046_long_begin, - PBEL(0x80|0x00|0x00|0x10|0x00) = parse_ietf_v1_or_Q046_long_begin, + PBEL(0x80|0x40|0x00|0x10|0x08) = parse_ietf_v1_or_Q046plus_long_begin, + PBEL(0x80|0x00|0x00|0x10|0x08) = parse_ietf_v1_or_Q046plus_long_begin, + PBEL(0x80|0x40|0x00|0x10|0x00) = parse_ietf_v1_or_Q046plus_long_begin, + PBEL(0x80|0x00|0x00|0x10|0x00) = parse_ietf_v1_or_Q046plus_long_begin, /* 1X10 XGGG: */ - PBEL(0x80|0x40|0x20|0x00|0x08) = parse_ietf_v1_or_Q046_long_begin, - PBEL(0x80|0x00|0x20|0x00|0x08) = parse_ietf_v1_or_Q046_long_begin, - PBEL(0x80|0x40|0x20|0x00|0x00) = parse_ietf_v1_or_Q046_long_begin, - PBEL(0x80|0x00|0x20|0x00|0x00) = parse_ietf_v1_or_Q046_long_begin, + PBEL(0x80|0x40|0x20|0x00|0x08) = parse_ietf_v1_or_Q046plus_long_begin, + PBEL(0x80|0x00|0x20|0x00|0x08) = parse_ietf_v1_or_Q046plus_long_begin, + PBEL(0x80|0x40|0x20|0x00|0x00) = parse_ietf_v1_or_Q046plus_long_begin, + PBEL(0x80|0x00|0x20|0x00|0x00) = parse_ietf_v1_or_Q046plus_long_begin, /* 01XX XGGG */ PBEL(0x00|0x40|0x00|0x00|0x00) = lsquic_ietf_v1_parse_packet_in_short_begin, PBEL(0x00|0x40|0x00|0x00|0x08) = lsquic_ietf_v1_parse_packet_in_short_begin, @@ -102,7 +107,6 @@ lsquic_parse_packet_in_server_begin (struct lsquic_packet_in *packet_in, } -/* This function does not support Q046 */ int lsquic_parse_packet_in_begin (lsquic_packet_in_t *packet_in, size_t length, int is_server, unsigned cid_len, struct packin_parse_state *state) @@ -111,10 +115,9 @@ lsquic_parse_packet_in_begin (lsquic_packet_in_t *packet_in, size_t length, { switch (packet_in->pi_data[0] & 0xC0) { - /* XXX Revisit this: does this logic check out? */ case 0xC0: case 0x80: - return lsquic_ietf_v1_parse_packet_in_long_begin(packet_in, + return parse_ietf_v1_or_Q046plus_long_begin(packet_in, length, is_server, cid_len, state); case 0x00: return lsquic_gquic_parse_packet_in_begin(packet_in, length, @@ -169,6 +172,27 @@ lsquic_Q046_parse_packet_in_begin (struct lsquic_packet_in *packet_in, } +int +lsquic_Q050_parse_packet_in_begin (struct lsquic_packet_in *packet_in, + size_t length, int is_server, unsigned cid_len, + struct packin_parse_state *state) +{ + assert(!is_server); + assert(cid_len == GQUIC_CID_LEN); + if (length > 0) + { + if (0 == (packet_in->pi_data[0] & 0x80)) + return lsquic_ietf_v1_parse_packet_in_short_begin(packet_in, length, + is_server, is_server ? cid_len : 0, state); + else + return lsquic_Q050_parse_packet_in_long_begin(packet_in, length, + is_server, cid_len, state); + } + else + return -1; +} + + /* TODO This function uses the full packet parsing functionality to get at * the CID. This is an overkill and could be optimized -- at the cost of * some code duplication, of course. diff --git a/src/liblsquic/lsquic_parse_common.h b/src/liblsquic/lsquic_parse_common.h index 1b1c2e9..130cfda 100644 --- a/src/liblsquic/lsquic_parse_common.h +++ b/src/liblsquic/lsquic_parse_common.h @@ -35,7 +35,7 @@ lsquic_Q046_parse_packet_in_begin (struct lsquic_packet_in *, struct packin_parse_state *); int -lsquic_Q046_parse_packet_in_begin (struct lsquic_packet_in *, +lsquic_Q050_parse_packet_in_begin (struct lsquic_packet_in *, size_t length, int is_server, unsigned cid_len, struct packin_parse_state *); @@ -83,7 +83,7 @@ lsquic_is_valid_iquic_hs_packet (const unsigned char *buf, size_t buf_sz, lsquic_ver_tag_t *tag); int -lsquic_is_valid_ietf_v1_or_Q046_hs_packet (const unsigned char *buf, +lsquic_is_valid_ietf_v1_or_Q046plus_hs_packet (const unsigned char *buf, size_t length, lsquic_ver_tag_t *tagp); /* Instead of just -1 like CHECK_SPACE(), this macro returns the number diff --git a/src/liblsquic/lsquic_parse_gquic_be.c b/src/liblsquic/lsquic_parse_gquic_be.c index 90faddf..31c3af9 100644 --- a/src/liblsquic/lsquic_parse_gquic_be.c +++ b/src/liblsquic/lsquic_parse_gquic_be.c @@ -990,6 +990,33 @@ gquic_be_gen_ack_frame (unsigned char *outbuf, size_t outbuf_sz, } +static int +gquic_be_gen_crypto_frame (unsigned char *buf, size_t buf_len, + uint64_t offset, size_t size, gcf_read_f gcf_read, void *stream) +{ + assert(0); + return -1; +} + + +static int +gquic_be_parse_crypto_frame (const unsigned char *buf, size_t rem_packet_sz, + struct stream_frame *stream_frame) +{ + assert(0); + return -1; +} + + +static void +gquic_be_packno_info (const struct lsquic_conn *lconn, + const struct lsquic_packet_out *packet_out, unsigned *packno_off, + unsigned *packno_len) +{ + assert(0); +} + + const struct parse_funcs lsquic_parse_funcs_gquic_Q039 = { .pf_gen_reg_pkt_header = gquic_be_gen_reg_pkt_header, @@ -1019,10 +1046,13 @@ const struct parse_funcs lsquic_parse_funcs_gquic_Q039 = .pf_read_float_time16 = gquic_be_read_float_time16, #endif .pf_generate_simple_prst = lsquic_generate_gquic_reset, - .pf_parse_frame_type = parse_frame_type_gquic_Q035_thru_Q039, - .pf_turn_on_fin = lsquic_turn_on_fin_Q035_thru_Q039, + .pf_parse_frame_type = lsquic_parse_frame_type_gquic_Q035_thru_Q046, + .pf_turn_on_fin = lsquic_turn_on_fin_Q035_thru_Q046, .pf_packout_size = lsquic_gquic_packout_size, .pf_packout_max_header_size = lsquic_gquic_packout_header_size, .pf_calc_packno_bits = lsquic_gquic_calc_packno_bits, .pf_packno_bits2len = lsquic_gquic_packno_bits2len, + .pf_gen_crypto_frame = gquic_be_gen_crypto_frame, + .pf_parse_crypto_frame = gquic_be_parse_crypto_frame, + .pf_packno_info = gquic_be_packno_info, }; diff --git a/src/liblsquic/lsquic_parse_gquic_common.c b/src/liblsquic/lsquic_parse_gquic_common.c index c1eab87..a1e085d 100644 --- a/src/liblsquic/lsquic_parse_gquic_common.c +++ b/src/liblsquic/lsquic_parse_gquic_common.c @@ -198,7 +198,7 @@ lsquic_generate_gquic_reset (const lsquic_cid_t *cidp, } -static const enum quic_frame_type byte2frame_type_Q035_thru_Q039[0x100] = +static const enum quic_frame_type byte2frame_type_Q035_thru_Q046[0x100] = { [0x00] = QUIC_FRAME_PADDING, [0x01] = QUIC_FRAME_RST_STREAM, @@ -460,14 +460,14 @@ static const enum quic_frame_type byte2frame_type_Q035_thru_Q039[0x100] = enum quic_frame_type -parse_frame_type_gquic_Q035_thru_Q039 (unsigned char b) +lsquic_parse_frame_type_gquic_Q035_thru_Q046 (unsigned char b) { - return byte2frame_type_Q035_thru_Q039[b]; + return byte2frame_type_Q035_thru_Q046[b]; } void -lsquic_turn_on_fin_Q035_thru_Q039 (unsigned char *stream_header) +lsquic_turn_on_fin_Q035_thru_Q046 (unsigned char *stream_header) { /* 1fdoooss */ *stream_header |= 0x40; diff --git a/src/liblsquic/lsquic_parse_ietf_v1.c b/src/liblsquic/lsquic_parse_ietf_v1.c index 17cbd27..9430443 100644 --- a/src/liblsquic/lsquic_parse_ietf_v1.c +++ b/src/liblsquic/lsquic_parse_ietf_v1.c @@ -38,6 +38,8 @@ #include "lsquic_conn.h" #include "lsquic_enc_sess.h" #include "lsquic_trans_params.h" +#include "lsquic_parse_ietf.h" +#include "lsquic_qtags.h" #define LSQUIC_LOGGER_MODULE LSQLM_PARSE #include "lsquic_logger.h" @@ -235,8 +237,7 @@ gen_long_pkt_header (const struct lsquic_conn *lconn, vint_write(p, payload_len, bits, 1 << bits); p += 1 << bits; - p += write_packno(p, packet_out->po_packno, - lsquic_packet_out_packno_bits(packet_out)); + p += write_packno(p, packet_out->po_packno, packno_bits); return p - buf; } @@ -431,9 +432,10 @@ ietf_v1_gen_stream_frame (unsigned char *buf, size_t buf_len, } -static int -ietf_v1_gen_crypto_frame (unsigned char *buf, size_t buf_len, - uint64_t offset, size_t size, gcf_read_f gcf_read, void *stream) +int +lsquic_ietf_v1_gen_crypto_frame (unsigned char *buf, unsigned char first_byte, + size_t buf_len, uint64_t offset, size_t size, gcf_read_f gcf_read, + void *stream) { unsigned char *const end = buf + buf_len; unsigned char *p; @@ -454,7 +456,7 @@ ietf_v1_gen_crypto_frame (unsigned char *buf, size_t buf_len, size = n_avail; p = buf; - *p++ = 0x06; + *p++ = first_byte; vint_write(p, offset, obits, olen); p += olen; @@ -470,6 +472,15 @@ ietf_v1_gen_crypto_frame (unsigned char *buf, size_t buf_len, } +static int +ietf_v1_gen_crypto_frame (unsigned char *buf, size_t buf_len, + uint64_t offset, size_t size, gcf_read_f gcf_read, void *stream) +{ + return lsquic_ietf_v1_gen_crypto_frame(buf, 0x6, buf_len, offset, + size, gcf_read, stream); +} + + /* return parsed (used) buffer length */ static int ietf_v1_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz, @@ -529,9 +540,9 @@ ietf_v1_parse_stream_frame (const unsigned char *buf, size_t rem_packet_sz, } -static int -ietf_v1_parse_crypto_frame (const unsigned char *buf, size_t rem_packet_sz, - struct stream_frame *stream_frame) +int +lsquic_ietf_v1_parse_crypto_frame (const unsigned char *buf, + size_t rem_packet_sz, struct stream_frame *stream_frame) { const unsigned char *const pend = buf + rem_packet_sz; const unsigned char *p = buf; @@ -540,7 +551,6 @@ ietf_v1_parse_crypto_frame (const unsigned char *buf, size_t rem_packet_sz, CHECK_SPACE(1, p, pend); - assert(0x06 == *p); ++p; r = vint_read(p, pend, &offset); @@ -568,6 +578,20 @@ ietf_v1_parse_crypto_frame (const unsigned char *buf, size_t rem_packet_sz, } +static int +ietf_v1_parse_crypto_frame (const unsigned char *buf, size_t rem_packet_sz, + struct stream_frame *stream_frame) +{ + if (rem_packet_sz > 0) + { + assert(0x06 == buf[0]); + return lsquic_ietf_v1_parse_crypto_frame(buf, rem_packet_sz, + stream_frame); + } + else + return -1; +} + #if __GNUC__ # define UNLIKELY(cond) __builtin_expect(cond, 0) @@ -1044,12 +1068,13 @@ ietf_v1_calc_stream_frame_header_sz (lsquic_stream_id_t stream_id, } +/* [draft-ietf-quic-transport-24] Section 19.6 */ static size_t -ietf_v1_calc_crypto_frame_header_sz (uint64_t offset) +ietf_v1_calc_crypto_frame_header_sz (uint64_t offset, unsigned data_sz) { return 1 /* Frame type */ + (1 << vint_val2bits(offset)) - + 1 /* Data len */ + + (1 << vint_val2bits(data_sz)) ; } @@ -1705,7 +1730,7 @@ lsquic_ietf_v1_parse_packet_in_long_begin (struct lsquic_packet_in *packet_in, /* Is this a valid Initial packet? We take the perspective of the server. */ int -lsquic_is_valid_ietf_v1_or_Q046_hs_packet (const unsigned char *buf, +lsquic_is_valid_ietf_v1_or_Q046plus_hs_packet (const unsigned char *buf, size_t length, lsquic_ver_tag_t *tagp) { const unsigned char *p = buf; @@ -1727,11 +1752,11 @@ lsquic_is_valid_ietf_v1_or_Q046_hs_packet (const unsigned char *buf, memcpy(&tag, p, 4); p += 4; - if (tag == 0) - return 0; /* Client never sends version negotiation packets */ - - if (tag == * (uint32_t *) "Q046") + switch (tag) { + case 0: + return 0; /* Client never sends version negotiation packets */ + case TAG('Q', '0', '4', '6'): dcil = p[0] >> 4; if (dcil) dcil += 3; @@ -1747,9 +1772,19 @@ lsquic_is_valid_ietf_v1_or_Q046_hs_packet (const unsigned char *buf, if (end - p < (ptrdiff_t) (dcil + scil + packet_len)) return 0; - } - else - { + break; + case TAG('Q', '0', '5', '0'): + dcil = *p++; + if (dcil != 8) + return 0; + if (p + dcil + 1 >= end) + return 0; + p += dcil; + scil = *p++; + if (scil != 0) + return 0; + goto read_token; + default: dcil = *p++; if (dcil < MIN_INITIAL_DCID_LEN || dcil > MAX_CID_LEN) return 0; @@ -1760,6 +1795,7 @@ lsquic_is_valid_ietf_v1_or_Q046_hs_packet (const unsigned char *buf, if (p + scil > end || scil > MAX_CID_LEN) return 0; p += scil; + read_token: r = vint_read(p, end, &token_len); if (r < 0) return 0; diff --git a/src/liblsquic/lsquic_stream.c b/src/liblsquic/lsquic_stream.c index f18a585..9a7ea03 100644 --- a/src/liblsquic/lsquic_stream.c +++ b/src/liblsquic/lsquic_stream.c @@ -319,10 +319,10 @@ stream_stream_frame_header_sz (const struct lsquic_stream *stream, static size_t stream_crypto_frame_header_sz (const struct lsquic_stream *stream, - unsigned data_sz_IGNORED) + unsigned data_sz) { return stream->conn_pub->lconn->cn_pf - ->pf_calc_crypto_frame_header_sz(stream->tosend_off); + ->pf_calc_crypto_frame_header_sz(stream->tosend_off, data_sz); } @@ -333,7 +333,7 @@ stream_is_hsk (const struct lsquic_stream *stream) if (stream->sm_bflags & SMBF_IETF) return 0; else - return stream->id == LSQUIC_GQUIC_STREAM_HANDSHAKE; + return lsquic_stream_is_crypto(stream); } @@ -366,7 +366,7 @@ stream_new_common (lsquic_stream_id_t id, struct lsquic_conn_public *conn_pub, STAILQ_INIT(&stream->sm_hq_frames); - stream->sm_bflags |= ctor_flags & ((1 << (N_SMBF_FLAGS - 1)) - 1); + stream->sm_bflags |= ctor_flags & ((1 << N_SMBF_FLAGS) - 1); if (conn_pub->lconn->cn_flags & LSCONN_SERVER) stream->sm_bflags |= SMBF_SERVER; @@ -374,10 +374,6 @@ stream_new_common (lsquic_stream_id_t id, struct lsquic_conn_public *conn_pub, } -/* TODO: The logic to figure out whether the stream is connection limited - * should be taken out of the constructor. The caller should specify this - * via one of enum stream_ctor_flags. - */ lsquic_stream_t * lsquic_stream_new (lsquic_stream_id_t id, struct lsquic_conn_public *conn_pub, @@ -411,10 +407,11 @@ lsquic_stream_new (lsquic_stream_id_t id, lsquic_stream_set_priority_internal(stream, LSQUIC_STREAM_DEFAULT_PRIO); stream->sm_write_to_packet = stream_write_to_packet_std; + stream->sm_frame_header_sz = stream_stream_frame_header_sz; } else { - if (lsquic_stream_id_is_critical(ctor_flags & SCF_HTTP, id)) + if (ctor_flags & SCF_CRITICAL) cfcw = NULL; else { @@ -427,17 +424,25 @@ lsquic_stream_new (lsquic_stream_id_t id, stream->sm_readable = stream_readable_http_gquic; else stream->sm_readable = stream_readable_non_http; - if (stream_is_hsk(stream)) - stream->sm_write_to_packet = stream_write_to_packet_hsk; + if (ctor_flags & SCF_CRYPTO_FRAMES) + { + stream->sm_frame_header_sz = stream_crypto_frame_header_sz; + stream->sm_write_to_packet = stream_write_to_packet_crypto; + } else - stream->sm_write_to_packet = stream_write_to_packet_std; + { + if (stream_is_hsk(stream)) + stream->sm_write_to_packet = stream_write_to_packet_hsk; + else + stream->sm_write_to_packet = stream_write_to_packet_std; + stream->sm_frame_header_sz = stream_stream_frame_header_sz; + } } lsquic_sfcw_init(&stream->fc, initial_window, cfcw, conn_pub, id); stream->max_send_off = initial_send_off; LSQ_DEBUG("created stream"); SM_HISTORY_APPEND(stream, SHE_CREATED); - stream->sm_frame_header_sz = stream_stream_frame_header_sz; if (ctor_flags & SCF_CALL_ON_NEW) lsquic_stream_call_on_new(stream); return stream; @@ -2877,6 +2882,7 @@ stream_write_to_packet_std (struct frame_gen_ctx *fg_ctx, const size_t size) } +/* Use for IETF crypto streams and gQUIC crypto stream for versions >= Q050. */ static enum swtp_status stream_write_to_packet_crypto (struct frame_gen_ctx *fg_ctx, const size_t size) { @@ -2886,9 +2892,14 @@ stream_write_to_packet_crypto (struct frame_gen_ctx *fg_ctx, const size_t size) unsigned crypto_header_sz, need_at_least; struct lsquic_packet_out *packet_out; unsigned short off; - const enum packnum_space pns = lsquic_enclev2pns[ crypto_level(stream) ]; + enum packnum_space pns; int len, s; + if (stream->sm_bflags & SMBF_IETF) + pns = lsquic_enclev2pns[ crypto_level(stream) ]; + else + pns = PNS_INIT; + assert(size > 0); crypto_header_sz = stream->sm_frame_header_sz(stream, size); need_at_least = crypto_header_sz + 1; @@ -2919,6 +2930,15 @@ stream_write_to_packet_crypto (struct frame_gen_ctx *fg_ctx, const size_t size) packet_out->po_flags |= PO_HELLO; + if (!(stream->sm_bflags & SMBF_IETF)) + { + const unsigned short before = packet_out->po_data_sz; + lsquic_packet_out_zero_pad(packet_out); + /* XXX: too hacky */ + if (before < packet_out->po_data_sz) + send_ctl->sc_bytes_scheduled += packet_out->po_data_sz - before; + } + check_flush_threshold(stream); return SWTP_OK; } @@ -3993,22 +4013,6 @@ lsquic_stream_get_hset (struct lsquic_stream *stream) } -/* GQUIC-only function */ -int -lsquic_stream_id_is_critical (int use_http, lsquic_stream_id_t stream_id) -{ - return stream_id == LSQUIC_GQUIC_STREAM_HANDSHAKE - || (stream_id == LSQUIC_GQUIC_STREAM_HEADERS && use_http); -} - - -int -lsquic_stream_is_critical (const struct lsquic_stream *stream) -{ - return stream->sm_bflags & SMBF_CRITICAL; -} - - void lsquic_stream_set_stream_if (struct lsquic_stream *stream, const struct lsquic_stream_if *stream_if, void *stream_if_ctx) diff --git a/src/liblsquic/lsquic_stream.h b/src/liblsquic/lsquic_stream.h index 033ab84..22b1448 100644 --- a/src/liblsquic/lsquic_stream.h +++ b/src/liblsquic/lsquic_stream.h @@ -2,9 +2,6 @@ #ifndef LSQUIC_STREAM_H #define LSQUIC_STREAM_H -#define LSQUIC_GQUIC_STREAM_HANDSHAKE 1 -#define LSQUIC_GQUIC_STREAM_HEADERS 3 - #define LSQUIC_STREAM_DEFAULT_PRIO 16 /* RFC 7540, Section 5.3.5 */ @@ -178,12 +175,13 @@ enum stream_b_flags SMBF_SERVER = 1 << 0, SMBF_IETF = 1 << 1, SMBF_USE_HEADERS = 1 << 2, - SMBF_CRYPTO = 1 << 3, + SMBF_CRYPTO = 1 << 3, /* Crypto stream: applies to both gQUIC and IETF QUIC */ SMBF_CRITICAL = 1 << 4, /* This is a critical stream */ SMBF_AUTOSWITCH = 1 << 5, SMBF_RW_ONCE = 1 << 6, /* When set, read/write events are dispatched once per call */ SMBF_CONN_LIMITED = 1 << 7, -#define N_SMBF_FLAGS 8 + SMBF_HEADERS = 1 << 8, /* Headers stream */ +#define N_SMBF_FLAGS 9 }; @@ -356,6 +354,7 @@ enum stream_ctor_flags SCF_USE_DI_HASH = (1 << (N_SMBF_FLAGS + 1)), /* Use hash-based data input. If not set, * the nocopy data input is used. */ + SCF_CRYPTO_FRAMES = (1 << (N_SMBF_FLAGS + 2)), /* Write CRYPTO frames */ SCF_DI_AUTOSWITCH = SMBF_AUTOSWITCH, /* Automatically switch between nocopy * and hash-based to data input for optimal * performance. @@ -364,6 +363,8 @@ enum stream_ctor_flags SCF_CRITICAL = SMBF_CRITICAL, /* This is a critical stream */ SCF_IETF = SMBF_IETF, SCF_HTTP = SMBF_USE_HEADERS, + SCF_CRYPTO = SMBF_CRYPTO, + SCF_HEADERS = SMBF_HEADERS, }; @@ -501,11 +502,9 @@ lsquic_stream_update_sfcw (lsquic_stream_t *, uint64_t max_off); int lsquic_stream_set_priority_internal (lsquic_stream_t *, unsigned priority); -int -lsquic_stream_id_is_critical (int use_http, lsquic_stream_id_t); +#define lsquic_stream_is_critical(s) ((s)->sm_bflags & SMBF_CRITICAL) -int -lsquic_stream_is_critical (const struct lsquic_stream *); +#define lsquic_stream_is_crypto(s) ((s)->sm_bflags & SMBF_CRYPTO) size_t lsquic_stream_mem_used (const struct lsquic_stream *); diff --git a/src/liblsquic/lsquic_version.c b/src/liblsquic/lsquic_version.c index 5fe2e80..915f3c6 100644 --- a/src/liblsquic/lsquic_version.c +++ b/src/liblsquic/lsquic_version.c @@ -11,6 +11,7 @@ static const unsigned char version_tags[N_LSQVER][4] = [LSQVER_039] = { 'Q', '0', '3', '9', }, [LSQVER_043] = { 'Q', '0', '4', '3', }, [LSQVER_046] = { 'Q', '0', '4', '6', }, + [LSQVER_050] = { 'Q', '0', '5', '0', }, #if LSQUIC_USE_Q098 [LSQVER_098] = { 'Q', '0', '9', '8', }, #endif @@ -49,6 +50,7 @@ const char *const lsquic_ver2str[N_LSQVER] = { [LSQVER_039] = "Q039", [LSQVER_043] = "Q043", [LSQVER_046] = "Q046", + [LSQVER_050] = "Q050", #if LSQUIC_USE_Q098 [LSQVER_098] = "Q098", #endif diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index 397d58d..fb7f654 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -30,7 +30,6 @@ SET(TESTS arr attq blocked_gquic_be - buf bw_sampler conn_close_gquic_be crypto_gen diff --git a/test/unittests/test_buf.c b/test/unittests/test_buf.c deleted file mode 100644 index 20dd346..0000000 --- a/test/unittests/test_buf.c +++ /dev/null @@ -1,44 +0,0 @@ -/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */ -#include -#include -#include -#ifdef WIN32 -#include -#endif - -#include "lsquic_buf.h" - - -int -main (void) -{ - struct lsquic_buf *buf; - int s; - - buf = lsquic_buf_create(10); - assert(buf); - - assert(0 == lsquic_buf_size(buf)); - assert(10 == lsquic_buf_avail(buf)); - assert(10 == lsquic_buf_capacity(buf)); - - s = lsquic_buf_append(NULL, NULL, 0); - assert(s < 0); - s = lsquic_buf_append(buf, (void *) 123, -1); - assert(s < 0); - - s = lsquic_buf_append(buf, "dude", 4); - assert(4 == s); - assert(4 == lsquic_buf_size(buf)); - assert(6 == lsquic_buf_avail(buf)); - assert(10 == lsquic_buf_capacity(buf)); - - s = lsquic_buf_append(buf, ", where is my car?!", 20); - assert(20 == s); - assert(4 + 20 == lsquic_buf_size(buf)); - - assert(0 == strcasecmp(lsquic_buf_begin(buf), "Dude, where is my car?!")); - /* Yeah, where's your car, dude? */ - - return 0; -} diff --git a/test/unittests/test_export_key.c b/test/unittests/test_export_key.c index 5021774..e6e57b2 100644 --- a/test/unittests/test_export_key.c +++ b/test/unittests/test_export_key.c @@ -424,15 +424,15 @@ static const struct export_key_test tests[] = { static void run_ekt_test (const struct export_key_test *test) { - int s; + int s, i; unsigned char client_key[0x100], server_key[0x100], client_iv[0x100], server_iv[0x100]; - /* XXX: sub_key is confusing -- why is it so large? */ - unsigned char sub_key[0x1000]; + unsigned char sub_key[32]; + unsigned char c_hp[16], s_hp[16]; /* Sanity check the test itself: */ assert(test->ekt_client_key_sz < sizeof(client_key)); @@ -440,23 +440,28 @@ run_ekt_test (const struct export_key_test *test) assert(test->ekt_server_iv_sz < sizeof(server_iv)); assert(test->ekt_client_iv_sz < sizeof(client_iv)); - s = export_key_material(test->ekt_ikm, (uint32_t)test->ekt_ikm_sz, - test->ekt_salt, (int)test->ekt_salt_sz, - test->ekt_context, (uint32_t)test->ekt_context_sz, - (uint16_t)test->ekt_client_key_sz, client_key, - (uint16_t)test->ekt_server_key_sz, server_key, - (uint16_t)test->ekt_client_iv_sz, client_iv, - (uint16_t)test->ekt_server_iv_sz, server_iv, - sub_key); - - assert(0 == s); /* This function always returns zero */ - - if (test->ekt_client_key_sz) - assert(0 == memcmp(client_key, test->ekt_client_key, - test->ekt_client_key_sz)); - if (test->ekt_server_key_sz) - assert(0 == memcmp(server_key, test->ekt_server_key, - test->ekt_server_key_sz)); + for (i = 0; i < 2; ++i) + { + s = lsquic_export_key_material(test->ekt_ikm, (uint32_t)test->ekt_ikm_sz, + test->ekt_salt, (int)test->ekt_salt_sz, + test->ekt_context, (uint32_t)test->ekt_context_sz, + (uint16_t)test->ekt_client_key_sz, client_key, + (uint16_t)test->ekt_server_key_sz, server_key, + (uint16_t)test->ekt_client_iv_sz, client_iv, + (uint16_t)test->ekt_server_iv_sz, server_iv, + sub_key, + /* Keys should not change because HP pointers are given */ + i ? c_hp : NULL, + i ? s_hp : NULL + ); + assert(0 == s); /* This function always returns zero */ + if (test->ekt_client_key_sz) + assert(0 == memcmp(client_key, test->ekt_client_key, + test->ekt_client_key_sz)); + if (test->ekt_server_key_sz) + assert(0 == memcmp(server_key, test->ekt_server_key, + test->ekt_server_key_sz)); + } } diff --git a/test/unittests/test_hkdf.c b/test/unittests/test_hkdf.c index ddd6a52..691f1ac 100644 --- a/test/unittests/test_hkdf.c +++ b/test/unittests/test_hkdf.c @@ -21,7 +21,7 @@ void test_HKDF() 32) == 0); L = 42; - lshkdf_expand(prk, (const unsigned char *) "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9", 10, L, okm, 0, NULL, 0, NULL, 0, NULL, 0, NULL); + lshkdf_expand(prk, (const unsigned char *) "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9", 10, L, okm, 0, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, NULL); assert(memcmp(okm, "\x3c\xb2\x5f\x25\xfa\xac\xd5\x7a\x90\x43\x4f\x64\xd0\x36\x2f\x2a" "\x2d\x2d\x0a\x90\xcf\x1a\x5a\x4c\x5d\xb0\x2d\x56\xec\xc4\xc5\xbf" "\x34\x00\x72\x08\xd5\xb8\x87\x18\x58\x65", L) == 0); @@ -52,7 +52,7 @@ void test_HKDF() "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef" "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", - 80, L, okm, 0, NULL, 0, NULL, 0, NULL, 0, NULL); + 80, L, okm, 0, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, NULL); assert(memcmp(okm, "\xb1\x1e\x39\x8d\xc8\x03\x27\xa1\xc8\xe7\xf7\x8c\x59\x6a\x49\x34" "\x4f\x01\x2e\xda\x2d\x4e\xfa\xd8\xa0\x50\xcc\x4c\x19\xaf\xa9\x7c" "\x59\x04\x5a\x99\xca\xc7\x82\x72\x71\xcb\x41\xc6\x5e\x59\x0e\x09" @@ -71,7 +71,7 @@ void test_HKDF() 32) == 0); L = 42; - lshkdf_expand(prk, NULL, 0, L, okm, 0, NULL, 0, NULL, 0, NULL, 0, NULL); + lshkdf_expand(prk, NULL, 0, L, okm, 0, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, NULL); assert(memcmp(okm, "\x8d\xa4\xe7\x75\xa5\x63\xc1\x8f\x71\x5f\x80\x2a\x06\x3c\x5a\x31" "\xb8\xa1\x1f\x5c\x5e\xe1\x87\x9e\xc3\x45\x4e\x5f\x3c\x73\x8d\x2d" "\x9d\x20\x13\x95\xfa\xa4\xb6\x1a\x96\xc8", L) == 0); diff --git a/test/unittests/test_spi.c b/test/unittests/test_spi.c index b758eb5..f9a0de2 100644 --- a/test/unittests/test_spi.c +++ b/test/unittests/test_spi.c @@ -158,8 +158,8 @@ struct stream_info const struct stream_info infos1[] = { - { LSQUIC_GQUIC_STREAM_HANDSHAKE, SMBF_CRITICAL, 0, }, - { LSQUIC_GQUIC_STREAM_HEADERS, SMBF_CRITICAL, 0, }, + { 1, SMBF_CRITICAL, 0, }, + { 3, SMBF_CRITICAL, 0, }, { 5, 0, 0, }, { 7, 0, 1, }, { 127, 0, 200, }, @@ -167,8 +167,8 @@ const struct stream_info infos1[] = { const struct stream_info infos2[] = { - { LSQUIC_GQUIC_STREAM_HANDSHAKE, SMBF_CRITICAL, 0, }, - { LSQUIC_GQUIC_STREAM_HEADERS, SMBF_CRITICAL, 0, }, + { 1, SMBF_CRITICAL, 0, }, + { 3, SMBF_CRITICAL, 0, }, { 5, 0, 4, }, { 7, 0, 1, }, { 127, 0, 200, }, diff --git a/test/unittests/test_stream.c b/test/unittests/test_stream.c index d7ce719..c59580e 100644 --- a/test/unittests/test_stream.c +++ b/test/unittests/test_stream.c @@ -426,14 +426,23 @@ new_frame_in (struct test_objs *tobjs, size_t off, size_t sz, int fin) static lsquic_stream_t * new_stream_ext (struct test_objs *tobjs, unsigned stream_id, uint64_t send_off) { + enum stream_ctor_flags ctor_flags; + if (g_use_crypto_ctor) return lsquic_stream_new_crypto(stream_id, &tobjs->conn_pub, tobjs->stream_if, tobjs->stream_if_ctx, tobjs->ctor_flags | SCF_CRITICAL); else + { + /* For the purposes of the unit test, consider streams 1 and 3 critical */ + if (stream_id == 3 || stream_id == 1) + ctor_flags = SCF_CRITICAL; + else + ctor_flags = 0; return lsquic_stream_new(stream_id, &tobjs->conn_pub, tobjs->stream_if, tobjs->stream_if_ctx, tobjs->initial_stream_window, send_off, - tobjs->ctor_flags); + tobjs->ctor_flags | ctor_flags); + } } @@ -1256,7 +1265,7 @@ test_unlimited_stream_flush_data (struct test_objs *tobjs) const struct lsquic_conn_cap *const cap = &tobjs->conn_pub.conn_cap; assert(0x4000 == lsquic_conn_cap_avail(cap)); /* Self-check */ - stream = new_stream(tobjs, LSQUIC_GQUIC_STREAM_HANDSHAKE); + stream = new_stream(tobjs, 1); n = lsquic_stream_write(stream, buf, 100); assert(n == 100); @@ -1540,7 +1549,7 @@ test_conn_unlimited (void) unsigned char *const data = calloc(1, 0x4000); /* Test 1: first write headers, then data stream */ - header_stream = new_stream(&tobjs, LSQUIC_GQUIC_STREAM_HANDSHAKE); + header_stream = new_stream(&tobjs, 1); data_stream = new_stream(&tobjs, 123); nw = lsquic_stream_write(header_stream, data, 98); assert(98 == nw); @@ -1552,7 +1561,7 @@ test_conn_unlimited (void) lsquic_stream_destroy(data_stream); /* Test 2: first write data, then headers stream */ - header_stream = new_stream(&tobjs, LSQUIC_GQUIC_STREAM_HANDSHAKE); + header_stream = new_stream(&tobjs, 1); data_stream = new_stream(&tobjs, 123); lsquic_conn_cap_init(&tobjs.conn_pub.conn_cap, 0x4000); nw = lsquic_stream_write(data_stream, data, 0x4000); @@ -2775,7 +2784,7 @@ test_window_update2 (void) init_test_objs(&tobjs, 0x4000, 0x4000, NULL); n_closed = 0; - stream = new_stream_ext(&tobjs, LSQUIC_GQUIC_STREAM_HANDSHAKE, 3); + stream = new_stream_ext(&tobjs, 1, 3); nw = lsquic_stream_write(stream, "1234567890", 10); lsquic_stream_flush(stream); assert(("lsquic_stream_write is limited by the send window", 3 == nw));