From 83287402d54d2970e405e7d06b44efd998957c59 Mon Sep 17 00:00:00 2001 From: Dmitri Tikhonov Date: Mon, 9 Oct 2017 07:52:09 -0400 Subject: [PATCH] Latest changes - Hide handshake implementation behind a set of function pointers - Use monotonically increasing clock - Make sure that retx delay is not larger than the max of 60 seconds --- CHANGELOG | 6 + src/liblsquic/lsquic_chsk_stream.c | 15 +- src/liblsquic/lsquic_conn.c | 4 +- src/liblsquic/lsquic_conn.h | 2 + src/liblsquic/lsquic_engine.c | 3 +- src/liblsquic/lsquic_full_conn.c | 23 ++- src/liblsquic/lsquic_global.c | 8 +- src/liblsquic/lsquic_handshake.c | 245 +++++++++++++++++++++++------ src/liblsquic/lsquic_handshake.h | 242 ++++++++-------------------- src/liblsquic/lsquic_int_types.h | 2 +- src/liblsquic/lsquic_send_ctl.c | 5 +- src/liblsquic/lsquic_util.c | 32 ++++ src/liblsquic/lsquic_util.h | 3 + 13 files changed, 343 insertions(+), 247 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d438ec0..9fbbcfb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +2017-10-09 + + - Hide handshake implementation behind a set of function pointers + - Use monotonically increasing clock + - Make sure that retx delay is not larger than the max of 60 seconds + 2017-09-29 - A few fixes to code and README diff --git a/src/liblsquic/lsquic_chsk_stream.c b/src/liblsquic/lsquic_chsk_stream.c index 34af0a8..bf46151 100644 --- a/src/liblsquic/lsquic_chsk_stream.c +++ b/src/liblsquic/lsquic_chsk_stream.c @@ -15,6 +15,7 @@ #include "lsquic_int_types.h" #include "lsquic.h" +#include "lsquic_str.h" #include "lsquic_handshake.h" #include "lsquic_chsk_stream.h" #include "lsquic_ver_neg.h" @@ -78,9 +79,9 @@ hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh) } c_hsk->buf_off += nread; - s = handle_chlo_reply(c_hsk->lconn->cn_enc_session, + s = c_hsk->lconn->cn_esf->esf_handle_chlo_reply(c_hsk->lconn->cn_enc_session, c_hsk->buf_in, c_hsk->buf_off); - LSQ_DEBUG("handle_chlo_reply returned %d", s); + LSQ_DEBUG("lsquic_enc_session_handle_chlo_reply returned %d", s); switch (s) { case DATA_NOT_ENOUGH: @@ -101,7 +102,7 @@ hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh) lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in); c_hsk->buf_in = NULL; lsquic_stream_wantread(stream, 0); - if (is_hs_done(c_hsk->lconn->cn_enc_session)) + if (c_hsk->lconn->cn_esf->esf_is_hsk_done(c_hsk->lconn->cn_enc_session)) { LSQ_DEBUG("handshake is complete, inform connection"); c_hsk->lconn->cn_if->ci_handshake_done(c_hsk->lconn); @@ -114,9 +115,9 @@ hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh) } break; default: - LSQ_WARN("handle_chlo_reply returned unknown value %d", s); + LSQ_WARN("lsquic_enc_session_handle_chlo_reply returned unknown value %d", s); case DATA_FORMAT_ERROR: - LSQ_INFO("handle_chlo_reply returned an error"); + LSQ_INFO("lsquic_enc_session_handle_chlo_reply returned an error"); break; } } @@ -144,8 +145,8 @@ hsk_client_on_write (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh) } len = 4 * 1024; - if (0 != gen_chlo(c_hsk->lconn->cn_enc_session, c_hsk->ver_neg->vn_ver, - buf, &len)) + if (0 != c_hsk->lconn->cn_esf->esf_gen_chlo(c_hsk->lconn->cn_enc_session, + c_hsk->ver_neg->vn_ver, buf, &len)) { LSQ_WARN("cannot create CHLO message"); lsquic_mm_put_4k(c_hsk->mm, buf); diff --git a/src/liblsquic/lsquic_conn.c b/src/liblsquic/lsquic_conn.c index 927638c..7d2529a 100644 --- a/src/liblsquic/lsquic_conn.c +++ b/src/liblsquic/lsquic_conn.c @@ -8,6 +8,7 @@ #include "lsquic_conn.h" #include "lsquic_packet_common.h" #include "lsquic_packet_in.h" +#include "lsquic_str.h" #include "lsquic_handshake.h" #include "lsquic_mm.h" #include "lsquic_engine_public.h" @@ -119,7 +120,8 @@ lsquic_conn_decrypt_packet (lsquic_conn_t *lconn, header_len = packet_in->pi_header_sz; data_len = packet_in->pi_data_sz - packet_in->pi_header_sz; - if (0 == lsquic_dec(lconn->cn_enc_session, lconn->cn_version, 0, + if (0 == lconn->cn_esf->esf_decrypt(lconn->cn_enc_session, + lconn->cn_version, 0, packet_in->pi_packno, packet_in->pi_data, &header_len, data_len, lsquic_packet_in_nonce(packet_in), diff --git a/src/liblsquic/lsquic_conn.h b/src/liblsquic/lsquic_conn.h index 993ccbb..422e7b3 100644 --- a/src/liblsquic/lsquic_conn.h +++ b/src/liblsquic/lsquic_conn.h @@ -88,6 +88,8 @@ struct lsquic_conn { void *cn_peer_ctx; struct lsquic_enc_session *cn_enc_session; + const struct enc_session_funcs + *cn_esf; lsquic_cid_t cn_cid; STAILQ_ENTRY(lsquic_conn) cn_next_closed_conn; TAILQ_ENTRY(lsquic_conn) cn_next_all, diff --git a/src/liblsquic/lsquic_engine.c b/src/liblsquic/lsquic_engine.c index 10ceedc..dcf40ac 100644 --- a/src/liblsquic/lsquic_engine.c +++ b/src/liblsquic/lsquic_engine.c @@ -41,6 +41,7 @@ #include "lsquic_full_conn.h" #include "lsquic_util.h" #include "lsquic_qtags.h" +#include "lsquic_str.h" #include "lsquic_handshake.h" #include "lsquic_mm.h" #include "lsquic_conn_hash.h" @@ -1072,7 +1073,7 @@ really_encrypt_packet (const lsquic_conn_t *conn, return -1; is_hello_packet = !!(packet_out->po_flags & PO_HELLO); - enc = lsquic_enc(conn->cn_enc_session, conn->cn_version, 0, + enc = conn->cn_esf->esf_encrypt(conn->cn_enc_session, conn->cn_version, 0, packet_out->po_packno, header_buf, header_sz, packet_out->po_data, packet_out->po_data_sz, buf, bufsz, &packet_sz, is_hello_packet); diff --git a/src/liblsquic/lsquic_full_conn.c b/src/liblsquic/lsquic_full_conn.c index 7d206bc..1ad5a4f 100644 --- a/src/liblsquic/lsquic_full_conn.c +++ b/src/liblsquic/lsquic_full_conn.c @@ -34,6 +34,8 @@ #include "lsquic_set.h" #include "lsquic_malo.h" #include "lsquic_chsk_stream.h" +#include "lsquic_str.h" +#include "lsquic_qtags.h" #include "lsquic_handshake.h" #include "lsquic_headers_stream.h" #include "lsquic_frame_common.h" @@ -325,7 +327,8 @@ send_smhl (const struct full_conn *conn) uint32_t smhl; return conn->fc_conn.cn_enc_session && (conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE) - && 0 == get_peer_setting(conn->fc_conn.cn_enc_session, QTAG_SMHL, &smhl) + && 0 == conn->fc_conn.cn_esf->esf_get_peer_setting( + conn->fc_conn.cn_enc_session, QTAG_SMHL, &smhl) && 1 == smhl; } @@ -387,8 +390,8 @@ apply_peer_settings (struct full_conn *conn) #endif for (n = 0; n < sizeof(tags) / sizeof(tags[0]); ++n) - if (0 != get_peer_setting(conn->fc_conn.cn_enc_session, - tags[n].tag, tags[n].val)) + if (0 != conn->fc_conn.cn_esf->esf_get_peer_setting( + conn->fc_conn.cn_enc_session, tags[n].tag, tags[n].val)) { LSQ_INFO("peer did not supply value for %s", tags[n].tag_str); return -1; @@ -521,15 +524,20 @@ full_conn_client_new (struct lsquic_engine_public *enpub, const char *hostname, unsigned short max_packet_size) { struct full_conn *conn; + enum lsquic_version version; lsquic_cid_t cid; + const struct enc_session_funcs *esf; - cid = generate_cid(); + version = highest_bit_set(enpub->enp_settings.es_versions); + esf = select_esf_by_ver(version); + cid = esf->esf_generate_cid(); conn = new_conn_common(cid, enpub, stream_if, stream_if_ctx, flags, max_packet_size); if (!conn) return NULL; - conn->fc_conn.cn_enc_session = new_enc_session_c(hostname, cid, - conn->fc_enpub); + conn->fc_conn.cn_esf = esf; + conn->fc_conn.cn_enc_session = + conn->fc_conn.cn_esf->esf_create_client(hostname, cid, conn->fc_enpub); if (!conn->fc_conn.cn_enc_session) { LSQ_WARN("could not create enc session: %s", strerror(errno)); @@ -547,7 +555,6 @@ full_conn_client_new (struct lsquic_engine_public *enpub, conn->fc_stream_ifs[STREAM_IF_HSK] .stream_if = &lsquic_client_hsk_stream_if; conn->fc_stream_ifs[STREAM_IF_HSK].stream_if_ctx = &conn->fc_hsk_ctx.client; - /* TODO the client does not know how to perform version negotiation */ init_ver_neg(conn, conn->fc_settings->es_versions); conn->fc_conn.cn_pf = select_pf_by_ver(conn->fc_ver_neg.vn_ver); if (conn->fc_settings->es_handshake_to) @@ -628,7 +635,7 @@ full_conn_ci_destroy (lsquic_conn_t *lconn) lsquic_send_ctl_cleanup(&conn->fc_send_ctl); lsquic_rechist_cleanup(&conn->fc_rechist); if (conn->fc_conn.cn_enc_session) - free_enc_session(conn->fc_conn.cn_enc_session); + conn->fc_conn.cn_esf->esf_destroy(conn->fc_conn.cn_enc_session); lsquic_malo_destroy(conn->fc_pub.packet_out_malo); #if FULL_CONN_STATS LSQ_NOTICE("received %u packets, of which %u were not decryptable, %u were " diff --git a/src/liblsquic/lsquic_global.c b/src/liblsquic/lsquic_global.c index 304a8f3..2e2eb5c 100644 --- a/src/liblsquic/lsquic_global.c +++ b/src/liblsquic/lsquic_global.c @@ -3,20 +3,24 @@ * Global state */ +#include "lsquic_int_types.h" #include "lsquic_types.h" #include "lsquic.h" +#include "lsquic_str.h" #include "lsquic_handshake.h" +#include "lsquic_util.h" int lsquic_global_init (int flags) { - return handshake_init(flags); + lsquic_init_timers(); + return lsquic_enc_session_gquic_1.esf_global_init(flags); } void lsquic_global_cleanup (void) { - handshake_cleanup(); + lsquic_enc_session_gquic_1.esf_global_cleanup(); } diff --git a/src/liblsquic/lsquic_handshake.c b/src/liblsquic/lsquic_handshake.c index 121b54f..3f6a4db 100644 --- a/src/liblsquic/lsquic_handshake.c +++ b/src/liblsquic/lsquic_handshake.c @@ -18,6 +18,7 @@ #include "lsquic.h" #include "lsquic_types.h" #include "lsquic_crypto.h" +#include "lsquic_str.h" #include "lsquic_handshake.h" #include "lsquic_parse.h" #include "lsquic_crt_compress.h" @@ -25,9 +26,9 @@ #include "lsquic_version.h" #include "lsquic_mm.h" #include "lsquic_engine_public.h" -#include "lsquic_str.h" #include "lsquic_hash.h" #include "lsquic_buf.h" +#include "lsquic_qtags.h" #include "fiu-local.h" @@ -38,6 +39,114 @@ #define LSQUIC_LOGGER_MODULE LSQLM_HANDSHAKE #include "lsquic_logger.h" +enum handshake_state +{ + HSK_CHLO_REJ = 0, + HSK_SHLO, + HSK_COMPLETED, + N_HSK_STATES +}; + +#if LSQUIC_KEEP_ENC_SESS_HISTORY +typedef unsigned char eshist_idx_t; + +enum enc_sess_history_event +{ + ESHE_EMPTY = '\0', + ESHE_SET_SNI = 'I', + ESHE_SET_SNO = 'O', + ESHE_SET_STK = 'K', + ESHE_SET_SCID = 'D', + ESHE_SET_PROF = 'P', +}; +#endif + + +typedef struct hs_ctx_st +{ + enum { + HSET_TCID = (1 << 0), /* tcid is set */ + HSET_SMHL = (1 << 1), /* smhl is set */ + HSET_SCID = (1 << 2), + } set; + enum { + HOPT_NSTP = (1 << 0), /* NSTP option present in COPT */ + HOPT_SREJ = (1 << 1), /* SREJ option present in COPT */ + } opts; + uint32_t pdmd; + uint32_t aead; + uint32_t kexs; + + uint32_t mids; + uint32_t scls; + uint32_t cfcw; + uint32_t sfcw; + uint32_t srbf; + uint32_t icsl; + + uint32_t irtt; + uint64_t rcid; + uint32_t tcid; + uint32_t smhl; + uint64_t ctim; /* any usage? */ + uint64_t sttl; + unsigned char scid[SCID_LENGTH]; + //unsigned char chlo_hash[32]; //SHA256 HASH of CHLO + unsigned char nonc[DNONC_LENGTH]; /* 4 tm, 8 orbit ---> REJ, 20 rand */ + unsigned char pubs[32]; + + uint32_t rrej; + struct lsquic_str ccs; + struct lsquic_str sni; /* 0 rtt */ + struct lsquic_str ccrt; + struct lsquic_str stk; + struct lsquic_str sno; + struct lsquic_str prof; + + struct lsquic_str csct; + struct lsquic_str crt; /* compressed certs buffer */ +} hs_ctx_t; + + +struct lsquic_enc_session +{ + enum handshake_state hsk_state; + + uint8_t have_key; /* 0, no 1, I, 2, D, 3, F */ + uint8_t peer_have_final_key; + uint8_t server_start_use_final_key; + + 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]; + + hs_ctx_t hs_ctx; + lsquic_session_cache_info_t *info; + SSL_CTX * ssl_ctx; + const struct lsquic_engine_public *enpub; + struct lsquic_str * cert_ptr; /* pointer to the leaf cert of the server, not real copy */ + struct lsquic_str chlo; /* real copy of CHLO message */ + struct lsquic_str sstk; + struct lsquic_str ssno; + +#if LSQUIC_KEEP_ENC_SESS_HISTORY + eshist_idx_t es_hist_idx; + unsigned char es_hist_buf[1 << ESHIST_BITS]; +#endif +}; + /*** * client side, it will store the domain/certs as cache cert @@ -49,6 +158,15 @@ static struct lsquic_hash *s_cached_client_certs; */ static struct lsquic_hash *s_cached_client_session_infos; +/* save to hash table */ +static lsquic_session_cache_info_t *retrieve_session_info_entry(const char *key); +static void remove_expire_session_info_entry(); +static void remove_session_info_entry(struct lsquic_str *key); + +static void free_info (lsquic_session_cache_info_t *); + +static void c_free_cert_hash_item (cert_hash_item_t *item); +static void c_free_cert_hash_item(cert_hash_item_t *item); static int get_tag_val_u32 (unsigned char *v, int len, uint32_t *val); @@ -74,8 +192,8 @@ eshist_append (lsquic_enc_session_t *enc_session, # define ESHIST_APPEND(sess, event) do { } while (0) #endif -int -handshake_init(int flags) +static int +lsquic_handshake_init(int flags) { crypto_init(); return init_hs_hash_tables(flags); @@ -114,8 +232,8 @@ cleanup_hs_hash_tables (void) } -void -handshake_cleanup (void) +static void +lsquic_handshake_cleanup (void) { cleanup_hs_hash_tables(); lsquic_crt_cleanup(); @@ -141,7 +259,8 @@ static int init_hs_hash_tables(int flags) /* client */ -cert_hash_item_t* c_find_certs(lsquic_str_t *domain) +cert_hash_item_t * +c_find_certs (const lsquic_str_t *domain) { struct lsquic_hash_elem *el; @@ -159,7 +278,8 @@ cert_hash_item_t* c_find_certs(lsquic_str_t *domain) /* client */ /* certs is an array of lsquic_str_t * */ -cert_hash_item_t *make_cert_hash_item(lsquic_str_t *domain, lsquic_str_t **certs, int count) +static cert_hash_item_t * +make_cert_hash_item (lsquic_str_t *domain, lsquic_str_t **certs, int count) { int i; uint64_t hash; @@ -180,7 +300,8 @@ cert_hash_item_t *make_cert_hash_item(lsquic_str_t *domain, lsquic_str_t **certs /* client */ -void c_free_cert_hash_item(cert_hash_item_t *item) +void +c_free_cert_hash_item (cert_hash_item_t *item) { int i; if (item) @@ -196,7 +317,8 @@ void c_free_cert_hash_item(cert_hash_item_t *item) /* client */ -int c_insert_certs(cert_hash_item_t *item) +static int +c_insert_certs (cert_hash_item_t *item) { if (lsquic_hash_insert(s_cached_client_certs, lsquic_str_cstr(item->domain), @@ -223,7 +345,8 @@ static int save_session_info_entry(lsquic_str_t *key, lsquic_session_cache_info_ /* If entry updated and need to remove cached entry */ -void remove_session_info_entry(lsquic_str_t *key) +static void +remove_session_info_entry (lsquic_str_t *key) { lsquic_session_cache_info_t *entry; struct lsquic_hash_elem *el; @@ -239,7 +362,7 @@ void remove_session_info_entry(lsquic_str_t *key) /* client */ -lsquic_session_cache_info_t * +static lsquic_session_cache_info_t * retrieve_session_info_entry (const char *key) { lsquic_session_cache_info_t *entry; @@ -262,7 +385,11 @@ retrieve_session_info_entry (const char *key) /* call it in timer() */ -void remove_expire_session_info_entry() +#if __GNUC__ +__attribute__((unused)) +#endif +static void +remove_expire_session_info_entry (void) { time_t tm = time(NULL); struct lsquic_hash_elem *el; @@ -280,8 +407,9 @@ void remove_expire_session_info_entry() } -lsquic_enc_session_t *new_enc_session_c(const char *domain, lsquic_cid_t cid, - const struct lsquic_engine_public *enpub) +static lsquic_enc_session_t * +lsquic_enc_session_create_client (const char *domain, lsquic_cid_t cid, + const struct lsquic_engine_public *enpub) { lsquic_session_cache_info_t *info; lsquic_enc_session_t *enc_session; @@ -318,7 +446,8 @@ lsquic_enc_session_t *new_enc_session_c(const char *domain, lsquic_cid_t cid, } -void free_enc_session(lsquic_enc_session_t *enc_session) +static void +lsquic_enc_session_destroy (lsquic_enc_session_t *enc_session) { if (!enc_session) return ; @@ -360,7 +489,8 @@ void free_enc_session(lsquic_enc_session_t *enc_session) } -void free_info(lsquic_session_cache_info_t *info) +static void +free_info (lsquic_session_cache_info_t *info) { lsquic_str_d(&info->sstk); lsquic_str_d(&info->scfg); @@ -376,7 +506,8 @@ static int get_hs_state(lsquic_enc_session_t *enc_session) /* make sure have more room for encrypt */ -int is_hs_done(lsquic_enc_session_t *enc_session) +static int +lsquic_enc_session_is_hsk_done (lsquic_enc_session_t *enc_session) { return (get_hs_state(enc_session) == HSK_COMPLETED); } @@ -672,7 +803,8 @@ generate_cid_buf (void *buf, size_t bufsz) } -lsquic_cid_t generate_cid(void) +static lsquic_cid_t +lsquic_generate_cid (void) { lsquic_cid_t cid; generate_cid_buf(&cid, sizeof(cid)); @@ -783,9 +915,9 @@ struct message_writer #define MSG_LEN_VAL(len) (+(len)) -int -gen_chlo (lsquic_enc_session_t *enc_session, enum lsquic_version version, - uint8_t *buf, size_t *len) +static int +lsquic_enc_session_gen_chlo (lsquic_enc_session_t *enc_session, + enum lsquic_version version, uint8_t *buf, size_t *len) { int ret, include_pad; const lsquic_str_t *const ccs = get_common_certs_hash(); @@ -944,7 +1076,7 @@ gen_chlo (lsquic_enc_session_t *enc_session, enum lsquic_version version, else ret = 0; - LSQ_DEBUG("gen_chlo called, return %d, buf_len %zd.", ret, *len); + LSQ_DEBUG("lsquic_enc_session_gen_chlo called, return %d, buf_len %zd.", ret, *len); return ret; } @@ -1000,7 +1132,8 @@ void setup_aead_ctx(EVP_AEAD_CTX **ctx, unsigned char key[], int key_len, } -int determine_diversification_key(lsquic_enc_session_t *enc_session, +static int +determine_diversification_key (lsquic_enc_session_t *enc_session, uint8_t *diversification_nonce ) { @@ -1177,8 +1310,9 @@ he2str (enum handshake_error he) * DATA_NOT_ENOUGH(-2) for not enough data, * DATA_FORMAT_ERROR(-1) all other errors */ -int handle_chlo_reply(lsquic_enc_session_t *enc_session, const uint8_t *data, - int len) +static int +lsquic_enc_session_handle_chlo_reply (lsquic_enc_session_t *enc_session, + const uint8_t *data, int len) { uint32_t head_tag; int ret; @@ -1278,7 +1412,7 @@ int handle_chlo_reply(lsquic_enc_session_t *enc_session, const uint8_t *data, } end: - LSQ_DEBUG("handle_chlo_reply called, buf in %d, return %d.", len, ret); + LSQ_DEBUG("lsquic_enc_session_handle_chlo_reply called, buf in %d, return %d.", len, ret); EV_LOG_CONN_EVENT(enc_session->cid, "%s returning %s", __func__, he2str(ret)); return ret; @@ -1413,28 +1547,20 @@ decrypt_packet (lsquic_enc_session_t *enc_session, uint8_t path_id, } -#ifndef NDEBUG -/* In debug builds, we need to be able to override this function for testing */ -__attribute__((weak)) -int +static int lsquic_enc_session_have_key_gt_one (const lsquic_enc_session_t *enc_session) { return enc_session && enc_session->have_key > 1; } -#endif - - -#ifndef NDEBUG -/* Use weak linkage so that tests can override this function */ - __attribute__((weak)) -#endif /* The size of `buf' is *header_len plus data_len. The two parts of the * buffer correspond to the header and the payload of incoming QUIC packet. */ /* 0 for OK, otherwise nonezero */ -int lsquic_dec(lsquic_enc_session_t *enc_session, enum lsquic_version version, +static int +lsquic_enc_session_decrypt (lsquic_enc_session_t *enc_session, + enum lsquic_version version, uint8_t path_id, uint64_t pack_num, unsigned char *buf, size_t *header_len, size_t data_len, unsigned char *diversification_nonce, @@ -1456,7 +1582,9 @@ int lsquic_dec(lsquic_enc_session_t *enc_session, enum lsquic_version version, } -int lsquic_enc(lsquic_enc_session_t *enc_session, enum lsquic_version version, +static int +lsquic_enc_session_encrypt (lsquic_enc_session_t *enc_session, + enum lsquic_version version, uint8_t path_id, uint64_t pack_num, const unsigned char *header, size_t header_len, const unsigned char *data, size_t data_len, @@ -1507,7 +1635,7 @@ int lsquic_enc(lsquic_enc_session_t *enc_session, enum lsquic_version version, ((IS_SERVER(enc_session)) && enc_session->server_start_use_final_key == 0)) { - LSQ_DEBUG("lsquic_enc using 'I' key..."); + LSQ_DEBUG("lsquic_enc_session_encrypt using 'I' key..."); key = enc_session->enc_ctx_i; memcpy(nonce, enc_session->enc_key_nonce_i, 4); if (is_shlo && enc_session->have_key == 3) @@ -1517,7 +1645,7 @@ int lsquic_enc(lsquic_enc_session_t *enc_session, enum lsquic_version version, } else { - LSQ_DEBUG("lsquic_enc using 'F' key..."); + LSQ_DEBUG("lsquic_enc_session_encrypt using 'F' key..."); key = enc_session->enc_ctx_f; memcpy(nonce, enc_session->enc_key_nonce_f, 4); } @@ -1536,8 +1664,9 @@ int lsquic_enc(lsquic_enc_session_t *enc_session, enum lsquic_version version, } -int -get_peer_option (const lsquic_enc_session_t *enc_session, uint32_t tag) +static int +lsquic_enc_session_get_peer_option (const lsquic_enc_session_t *enc_session, + uint32_t tag) { switch (tag) { @@ -1555,9 +1684,9 @@ get_peer_option (const lsquic_enc_session_t *enc_session, uint32_t tag) /* Query a several parameters sent by the peer that are required by * connection. */ -int -get_peer_setting (const lsquic_enc_session_t *enc_session, uint32_t tag, - uint32_t *val) +static int +lsquic_enc_session_get_peer_setting (const lsquic_enc_session_t *enc_session, + uint32_t tag, uint32_t *val) { switch (tag) { @@ -1616,7 +1745,7 @@ get_peer_setting (const lsquic_enc_session_t *enc_session, uint32_t tag, #if LSQUIC_KEEP_ENC_SESS_HISTORY -void +static void lsquic_get_enc_hist (const lsquic_enc_session_t *enc_session, char buf[(1 << ESHIST_BITS) + 1]) { @@ -1635,3 +1764,25 @@ lsquic_get_enc_hist (const lsquic_enc_session_t *enc_session, #endif + +#ifdef NDEBUG +const +#endif +struct enc_session_funcs lsquic_enc_session_gquic_1 = +{ + .esf_global_init = lsquic_handshake_init, + .esf_global_cleanup = lsquic_handshake_cleanup, +#if LSQUIC_KEEP_ENC_SESS_HISTORY + .esf_get_hist = lsquic_get_enc_hist, +#endif + .esf_destroy = lsquic_enc_session_destroy, + .esf_is_hsk_done = lsquic_enc_session_is_hsk_done, + .esf_encrypt = lsquic_enc_session_encrypt, + .esf_decrypt = lsquic_enc_session_decrypt, + .esf_get_peer_setting = lsquic_enc_session_get_peer_setting, + .esf_get_peer_option = lsquic_enc_session_get_peer_option, + .esf_create_client = lsquic_enc_session_create_client, + .esf_generate_cid = lsquic_generate_cid, + .esf_gen_chlo = lsquic_enc_session_gen_chlo, + .esf_handle_chlo_reply = lsquic_enc_session_handle_chlo_reply, +}; diff --git a/src/liblsquic/lsquic_handshake.h b/src/liblsquic/lsquic_handshake.h index f5e1270..b177a0b 100644 --- a/src/liblsquic/lsquic_handshake.h +++ b/src/liblsquic/lsquic_handshake.h @@ -1,18 +1,11 @@ /* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */ -#ifndef LSQUIC_HANDSHAKE_H -#define LSQUIC_HANDSHAKE_H - -#include -#include -#include - -#include -#include "lsquic_str.h" +#ifndef LSQUIC_HANDSHAKE_SERVER_H +#define LSQUIC_HANDSHAKE_SERVER_H struct lsquic_engine_public; -struct sockaddr; +struct lsquic_enc_session; -#include "lsquic_qtags.h" +typedef struct lsquic_enc_session lsquic_enc_session_t; #define STK_LENGTH 60 #define SNO_LENGTH 56 @@ -21,16 +14,6 @@ struct sockaddr; #define aes128_key_len 16 #define aes128_iv_len 4 -/* client side, certs and hashs - */ -typedef struct cert_hash_item_st -{ - struct lsquic_str* domain; /*with port, such as "xyz.com:8088" as the key */ - struct lsquic_str* crts; - struct lsquic_str* hashs; - int count; -} cert_hash_item_t; - enum handshake_error /* TODO: rename this enum */ { DATA_NOT_ENOUGH = -2, @@ -42,66 +25,6 @@ enum handshake_error /* TODO: rename this enum */ HS_2RTT = 2, }; -enum handshake_state -{ - HSK_CHLO_REJ = 0, - HSK_SHLO, - HSK_COMPLETED, - N_HSK_STATES -}; - -typedef struct tag_value_st -{ - uint32_t tag; - const char * value; - int len; -} tag_value_t; - -typedef struct hs_ctx_st -{ - enum { - HSET_TCID = (1 << 0), /* tcid is set */ - HSET_SMHL = (1 << 1), /* smhl is set */ - HSET_SCID = (1 << 2), - } set; - enum { - HOPT_NSTP = (1 << 0), /* NSTP option present in COPT */ - HOPT_SREJ = (1 << 1), /* SREJ option present in COPT */ - } opts; - uint32_t pdmd; - uint32_t aead; - uint32_t kexs; - - uint32_t mids; - uint32_t scls; - uint32_t cfcw; - uint32_t sfcw; - uint32_t srbf; - uint32_t icsl; - - uint32_t irtt; - uint64_t rcid; - uint32_t tcid; - uint32_t smhl; - uint64_t ctim; /* any usage? */ - uint64_t sttl; - unsigned char scid[SCID_LENGTH]; - //unsigned char chlo_hash[32]; //SHA256 HASH of CHLO - unsigned char nonc[DNONC_LENGTH]; /* 4 tm, 8 orbit ---> REJ, 20 rand */ - unsigned char pubs[32]; - - uint32_t rrej; - struct lsquic_str ccs; - struct lsquic_str sni; /* 0 rtt */ - struct lsquic_str ccrt; - struct lsquic_str stk; - struct lsquic_str sno; - struct lsquic_str prof; - - struct lsquic_str csct; - struct lsquic_str crt; /* compressed certs buffer */ -} hs_ctx_t; - /* client side need to store 0rtt info per STK */ typedef struct lsquic_session_cache_info_st { @@ -129,126 +52,89 @@ typedef struct lsquic_session_cache_info_st #endif #if LSQUIC_KEEP_ENC_SESS_HISTORY - #define ESHIST_BITS 7 #define ESHIST_MASK ((1 << ESHIST_BITS) - 1) #define ESHIST_STR_SIZE ((1 << ESHIST_BITS) + 1) -typedef unsigned char eshist_idx_t; - -enum enc_sess_history_event -{ - ESHE_EMPTY = '\0', - ESHE_SET_SNI = 'I', - ESHE_SET_SNO = 'O', - ESHE_SET_STK = 'K', - ESHE_SET_SCID = 'D', - ESHE_SET_PROF = 'P', -}; - #endif -typedef struct lsquic_enc_session +struct enc_session_funcs { - enum handshake_state hsk_state; - - uint8_t have_key; /* 0, no 1, I, 2, D, 3, F */ - uint8_t peer_have_final_key; - uint8_t server_start_use_final_key; + /* Global initialization: call once per implementation */ + int (*esf_global_init)(int flags); - 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]; - - hs_ctx_t hs_ctx; - lsquic_session_cache_info_t *info; - SSL_CTX * ssl_ctx; - const struct lsquic_engine_public *enpub; - struct lsquic_str * cert_ptr; /* pointer to the leaf cert of the server, not real copy */ - struct lsquic_str chlo; /* real copy of CHLO message */ - struct lsquic_str sstk; - struct lsquic_str ssno; + /* Global cleanup: call once per implementation */ + void (*esf_global_cleanup) (void); #if LSQUIC_KEEP_ENC_SESS_HISTORY - eshist_idx_t es_hist_idx; - unsigned char es_hist_buf[1 << ESHIST_BITS]; -#endif -} lsquic_enc_session_t; - -#if LSQUIC_KEEP_ENC_SESS_HISTORY -void -lsquic_get_enc_hist (const lsquic_enc_session_t *, char buf[ESHIST_STR_SIZE]); + /* Grab encryption session history */ + void (*esf_get_hist) (const lsquic_enc_session_t *, + char buf[ESHIST_STR_SIZE]); #endif -int handshake_init(int flags); -void handshake_cleanup(); + /* Destroy enc session */ + void (*esf_destroy)(lsquic_enc_session_t *enc_session); -lsquic_enc_session_t * -new_enc_session_c(const char *domain, lsquic_cid_t cid, - const struct lsquic_engine_public *); + /* Return true if handshake has been completed */ + int (*esf_is_hsk_done)(lsquic_enc_session_t *enc_session); -void free_enc_session(lsquic_enc_session_t *enc_session); -void free_info(lsquic_session_cache_info_t *info); - -lsquic_cid_t generate_cid(void); - -/* save to hash table */ -lsquic_session_cache_info_t *retrieve_session_info_entry(const char *key); -void remove_expire_session_info_entry(); -void remove_session_info_entry(struct lsquic_str *key); - -cert_hash_item_t *make_cert_hash_item(struct lsquic_str *domain, struct lsquic_str **certs, int count); -void c_free_cert_hash_item(cert_hash_item_t *item); -cert_hash_item_t* c_find_certs(struct lsquic_str *domain); -int c_insert_certs(cert_hash_item_t *item); - -/* -1 error, 0, OK, response in `buf' */ -int gen_chlo(lsquic_enc_session_t *enc_session, enum lsquic_version, - uint8_t *buf, size_t *len); -int handle_chlo_reply(lsquic_enc_session_t *enc_session, const uint8_t *data, - int len); - -int is_hs_done(lsquic_enc_session_t *enc_session); - -/** - * The belows are global functions - */ - -int lsquic_enc(lsquic_enc_session_t *enc_session, enum lsquic_version, + /* Encrypt buffer */ + int (*esf_encrypt)(lsquic_enc_session_t *enc_session, enum lsquic_version, uint8_t path_id, uint64_t pack_num, const unsigned char *header, size_t header_len, const unsigned char *data, size_t data_len, unsigned char *buf_out, size_t max_out_len, size_t *out_len, int is_hello); -int lsquic_dec(lsquic_enc_session_t *enc_session, enum lsquic_version, - uint8_t path_id, uint64_t pack_num, - unsigned char *buf, size_t *header_len, size_t data_len, - unsigned char *diversification_nonce, - unsigned char *buf_out, size_t max_out_len, size_t *out_len); + /* Decrypt buffer */ + int (*esf_decrypt)(lsquic_enc_session_t *enc_session, enum lsquic_version, + uint8_t path_id, uint64_t pack_num, + unsigned char *buf, size_t *header_len, size_t data_len, + unsigned char *diversification_nonce, + unsigned char *buf_out, size_t max_out_len, size_t *out_len); -int -get_peer_setting (const lsquic_enc_session_t *, uint32_t tag, uint32_t *val); + /* Get value of setting specified by `tag' */ + int (*esf_get_peer_setting) (const lsquic_enc_session_t *, uint32_t tag, + uint32_t *val); -int -get_peer_option (const lsquic_enc_session_t *enc_session, uint32_t tag); + /* Get value of peer option (that from COPT array) */ + int (*esf_get_peer_option) (const lsquic_enc_session_t *enc_session, + uint32_t tag); + /* Create client session */ + lsquic_enc_session_t * + (*esf_create_client) (const char *domain, lsquic_cid_t cid, + const struct lsquic_engine_public *); + + /* Generate connection ID */ + lsquic_cid_t (*esf_generate_cid) (void); + + /* -1 error, 0, OK, response in `buf' */ + int + (*esf_gen_chlo) (lsquic_enc_session_t *, enum lsquic_version, + uint8_t *buf, size_t *len); + + int + (*esf_handle_chlo_reply) (lsquic_enc_session_t *, + const uint8_t *data, int len); +}; + +extern #ifdef NDEBUG -#define lsquic_enc_session_have_key_gt_one(e) ((e) && (e)->have_key > 1) -#else -int -lsquic_enc_session_have_key_gt_one (const lsquic_enc_session_t *enc_session); +const #endif +struct enc_session_funcs lsquic_enc_session_gquic_1; + +#define select_esf_by_ver(ver) \ + (ver ? &lsquic_enc_session_gquic_1 : &lsquic_enc_session_gquic_1) + +/* client side, certs and hashs + */ +typedef struct cert_hash_item_st +{ + struct lsquic_str* domain; /*with port, such as "xyz.com:8088" as the key */ + struct lsquic_str* crts; + struct lsquic_str* hashs; + int count; +} cert_hash_item_t; #endif diff --git a/src/liblsquic/lsquic_int_types.h b/src/liblsquic/lsquic_int_types.h index 7eda70a..88bf6dd 100644 --- a/src/liblsquic/lsquic_int_types.h +++ b/src/liblsquic/lsquic_int_types.h @@ -8,7 +8,7 @@ #include -typedef uint64_t lsquic_time_t; /* Microseconds since the epoch */ +typedef uint64_t lsquic_time_t; /* Microseconds since some time */ typedef uint64_t lsquic_packno_t; typedef uint32_t lsquic_ver_tag_t; /* Opaque 4-byte value */ diff --git a/src/liblsquic/lsquic_send_ctl.c b/src/liblsquic/lsquic_send_ctl.c index 2a59758..c0358c1 100644 --- a/src/liblsquic/lsquic_send_ctl.c +++ b/src/liblsquic/lsquic_send_ctl.c @@ -235,8 +235,6 @@ calculate_packet_rto (lsquic_send_ctl_t *ctl) exp = MAX_RTO_BACKOFFS; delay = delay * (1 << exp); - if (delay > MAX_RTO_DELAY) - delay = MAX_RTO_DELAY; return delay; } @@ -306,6 +304,9 @@ set_retx_alarm (lsquic_send_ctl_t *ctl) break; } + if (delay > MAX_RTO_DELAY) + delay = MAX_RTO_DELAY; + LSQ_DEBUG("set retx alarm to %"PRIu64", which is %"PRIu64 " usec from now, mode %s", now + delay, delay, retx2str[rm]); lsquic_alarmset_set(ctl->sc_alset, AL_RETX, now + delay); diff --git a/src/liblsquic/lsquic_util.c b/src/liblsquic/lsquic_util.c index b21a9b4..02ca2fc 100644 --- a/src/liblsquic/lsquic_util.c +++ b/src/liblsquic/lsquic_util.c @@ -8,17 +8,49 @@ #include #include #include +#include +#include + +#if !(defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) && defined(__APPLE__) +#include +#endif #include "lsquic_int_types.h" #include "lsquic_util.h" +#if defined(__APPLE__) +static mach_timebase_info_data_t timebase; +#endif + +void +lsquic_init_timers (void) +{ +#if defined(__APPLE__) + mach_timebase_info(&timebase); +#endif +} + + lsquic_time_t lsquic_time_now (void) { +#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 + struct timespec ts; + (void) clock_gettime(CLOCK_MONOTONIC, &ts); + return (lsquic_time_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000; +#elif defined(__APPLE__) + lsquic_time_t t = mach_absolute_time(); + t *= timebase.numer; + t /= timebase.denom; + t /= 1000; + return t; +#else +# warn Monotonically increasing clock is not available on this platform struct timeval tv; (void) gettimeofday(&tv, NULL); return (lsquic_time_t) tv.tv_sec * 1000000 + tv.tv_usec; +#endif } diff --git a/src/liblsquic/lsquic_util.h b/src/liblsquic/lsquic_util.h index 8a11223..4109394 100644 --- a/src/liblsquic/lsquic_util.h +++ b/src/liblsquic/lsquic_util.h @@ -13,6 +13,9 @@ extern "C" { lsquic_time_t lsquic_time_now (void); +void +lsquic_init_timers (void); + /* Returns 1 if `buf' contains only zero bytes, 0 otherwise. */ int