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
This commit is contained in:
Dmitri Tikhonov 2017-10-09 07:52:09 -04:00
parent 0fb9ea94ae
commit 83287402d5
13 changed files with 343 additions and 247 deletions

View file

@ -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 2017-09-29
- A few fixes to code and README - A few fixes to code and README

View file

@ -15,6 +15,7 @@
#include "lsquic_int_types.h" #include "lsquic_int_types.h"
#include "lsquic.h" #include "lsquic.h"
#include "lsquic_str.h"
#include "lsquic_handshake.h" #include "lsquic_handshake.h"
#include "lsquic_chsk_stream.h" #include "lsquic_chsk_stream.h"
#include "lsquic_ver_neg.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; 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); 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) switch (s)
{ {
case DATA_NOT_ENOUGH: 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); lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
c_hsk->buf_in = NULL; c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0); 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"); LSQ_DEBUG("handshake is complete, inform connection");
c_hsk->lconn->cn_if->ci_handshake_done(c_hsk->lconn); 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; break;
default: 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: case DATA_FORMAT_ERROR:
LSQ_INFO("handle_chlo_reply returned an error"); LSQ_INFO("lsquic_enc_session_handle_chlo_reply returned an error");
break; break;
} }
} }
@ -144,8 +145,8 @@ hsk_client_on_write (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh)
} }
len = 4 * 1024; len = 4 * 1024;
if (0 != gen_chlo(c_hsk->lconn->cn_enc_session, c_hsk->ver_neg->vn_ver, if (0 != c_hsk->lconn->cn_esf->esf_gen_chlo(c_hsk->lconn->cn_enc_session,
buf, &len)) c_hsk->ver_neg->vn_ver, buf, &len))
{ {
LSQ_WARN("cannot create CHLO message"); LSQ_WARN("cannot create CHLO message");
lsquic_mm_put_4k(c_hsk->mm, buf); lsquic_mm_put_4k(c_hsk->mm, buf);

View file

@ -8,6 +8,7 @@
#include "lsquic_conn.h" #include "lsquic_conn.h"
#include "lsquic_packet_common.h" #include "lsquic_packet_common.h"
#include "lsquic_packet_in.h" #include "lsquic_packet_in.h"
#include "lsquic_str.h"
#include "lsquic_handshake.h" #include "lsquic_handshake.h"
#include "lsquic_mm.h" #include "lsquic_mm.h"
#include "lsquic_engine_public.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; header_len = packet_in->pi_header_sz;
data_len = packet_in->pi_data_sz - 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, packet_in->pi_packno, packet_in->pi_data,
&header_len, data_len, &header_len, data_len,
lsquic_packet_in_nonce(packet_in), lsquic_packet_in_nonce(packet_in),

View file

@ -88,6 +88,8 @@ struct lsquic_conn
{ {
void *cn_peer_ctx; void *cn_peer_ctx;
struct lsquic_enc_session *cn_enc_session; struct lsquic_enc_session *cn_enc_session;
const struct enc_session_funcs
*cn_esf;
lsquic_cid_t cn_cid; lsquic_cid_t cn_cid;
STAILQ_ENTRY(lsquic_conn) cn_next_closed_conn; STAILQ_ENTRY(lsquic_conn) cn_next_closed_conn;
TAILQ_ENTRY(lsquic_conn) cn_next_all, TAILQ_ENTRY(lsquic_conn) cn_next_all,

View file

@ -41,6 +41,7 @@
#include "lsquic_full_conn.h" #include "lsquic_full_conn.h"
#include "lsquic_util.h" #include "lsquic_util.h"
#include "lsquic_qtags.h" #include "lsquic_qtags.h"
#include "lsquic_str.h"
#include "lsquic_handshake.h" #include "lsquic_handshake.h"
#include "lsquic_mm.h" #include "lsquic_mm.h"
#include "lsquic_conn_hash.h" #include "lsquic_conn_hash.h"
@ -1072,7 +1073,7 @@ really_encrypt_packet (const lsquic_conn_t *conn,
return -1; return -1;
is_hello_packet = !!(packet_out->po_flags & PO_HELLO); 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_packno, header_buf, header_sz,
packet_out->po_data, packet_out->po_data_sz, packet_out->po_data, packet_out->po_data_sz,
buf, bufsz, &packet_sz, is_hello_packet); buf, bufsz, &packet_sz, is_hello_packet);

View file

@ -34,6 +34,8 @@
#include "lsquic_set.h" #include "lsquic_set.h"
#include "lsquic_malo.h" #include "lsquic_malo.h"
#include "lsquic_chsk_stream.h" #include "lsquic_chsk_stream.h"
#include "lsquic_str.h"
#include "lsquic_qtags.h"
#include "lsquic_handshake.h" #include "lsquic_handshake.h"
#include "lsquic_headers_stream.h" #include "lsquic_headers_stream.h"
#include "lsquic_frame_common.h" #include "lsquic_frame_common.h"
@ -325,7 +327,8 @@ send_smhl (const struct full_conn *conn)
uint32_t smhl; uint32_t smhl;
return conn->fc_conn.cn_enc_session return conn->fc_conn.cn_enc_session
&& (conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE) && (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; && 1 == smhl;
} }
@ -387,8 +390,8 @@ apply_peer_settings (struct full_conn *conn)
#endif #endif
for (n = 0; n < sizeof(tags) / sizeof(tags[0]); ++n) for (n = 0; n < sizeof(tags) / sizeof(tags[0]); ++n)
if (0 != get_peer_setting(conn->fc_conn.cn_enc_session, if (0 != conn->fc_conn.cn_esf->esf_get_peer_setting(
tags[n].tag, tags[n].val)) 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); LSQ_INFO("peer did not supply value for %s", tags[n].tag_str);
return -1; return -1;
@ -521,15 +524,20 @@ full_conn_client_new (struct lsquic_engine_public *enpub,
const char *hostname, unsigned short max_packet_size) const char *hostname, unsigned short max_packet_size)
{ {
struct full_conn *conn; struct full_conn *conn;
enum lsquic_version version;
lsquic_cid_t cid; 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, conn = new_conn_common(cid, enpub, stream_if, stream_if_ctx, flags,
max_packet_size); max_packet_size);
if (!conn) if (!conn)
return NULL; return NULL;
conn->fc_conn.cn_enc_session = new_enc_session_c(hostname, cid, conn->fc_conn.cn_esf = esf;
conn->fc_enpub); 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) if (!conn->fc_conn.cn_enc_session)
{ {
LSQ_WARN("could not create enc session: %s", strerror(errno)); 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] conn->fc_stream_ifs[STREAM_IF_HSK]
.stream_if = &lsquic_client_hsk_stream_if; .stream_if = &lsquic_client_hsk_stream_if;
conn->fc_stream_ifs[STREAM_IF_HSK].stream_if_ctx = &conn->fc_hsk_ctx.client; 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); init_ver_neg(conn, conn->fc_settings->es_versions);
conn->fc_conn.cn_pf = select_pf_by_ver(conn->fc_ver_neg.vn_ver); conn->fc_conn.cn_pf = select_pf_by_ver(conn->fc_ver_neg.vn_ver);
if (conn->fc_settings->es_handshake_to) 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_send_ctl_cleanup(&conn->fc_send_ctl);
lsquic_rechist_cleanup(&conn->fc_rechist); lsquic_rechist_cleanup(&conn->fc_rechist);
if (conn->fc_conn.cn_enc_session) 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); lsquic_malo_destroy(conn->fc_pub.packet_out_malo);
#if FULL_CONN_STATS #if FULL_CONN_STATS
LSQ_NOTICE("received %u packets, of which %u were not decryptable, %u were " LSQ_NOTICE("received %u packets, of which %u were not decryptable, %u were "

View file

@ -3,20 +3,24 @@
* Global state * Global state
*/ */
#include "lsquic_int_types.h"
#include "lsquic_types.h" #include "lsquic_types.h"
#include "lsquic.h" #include "lsquic.h"
#include "lsquic_str.h"
#include "lsquic_handshake.h" #include "lsquic_handshake.h"
#include "lsquic_util.h"
int int
lsquic_global_init (int flags) lsquic_global_init (int flags)
{ {
return handshake_init(flags); lsquic_init_timers();
return lsquic_enc_session_gquic_1.esf_global_init(flags);
} }
void void
lsquic_global_cleanup (void) lsquic_global_cleanup (void)
{ {
handshake_cleanup(); lsquic_enc_session_gquic_1.esf_global_cleanup();
} }

View file

@ -18,6 +18,7 @@
#include "lsquic.h" #include "lsquic.h"
#include "lsquic_types.h" #include "lsquic_types.h"
#include "lsquic_crypto.h" #include "lsquic_crypto.h"
#include "lsquic_str.h"
#include "lsquic_handshake.h" #include "lsquic_handshake.h"
#include "lsquic_parse.h" #include "lsquic_parse.h"
#include "lsquic_crt_compress.h" #include "lsquic_crt_compress.h"
@ -25,9 +26,9 @@
#include "lsquic_version.h" #include "lsquic_version.h"
#include "lsquic_mm.h" #include "lsquic_mm.h"
#include "lsquic_engine_public.h" #include "lsquic_engine_public.h"
#include "lsquic_str.h"
#include "lsquic_hash.h" #include "lsquic_hash.h"
#include "lsquic_buf.h" #include "lsquic_buf.h"
#include "lsquic_qtags.h"
#include "fiu-local.h" #include "fiu-local.h"
@ -38,6 +39,114 @@
#define LSQUIC_LOGGER_MODULE LSQLM_HANDSHAKE #define LSQUIC_LOGGER_MODULE LSQLM_HANDSHAKE
#include "lsquic_logger.h" #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 * 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; 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); 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) # define ESHIST_APPEND(sess, event) do { } while (0)
#endif #endif
int static int
handshake_init(int flags) lsquic_handshake_init(int flags)
{ {
crypto_init(); crypto_init();
return init_hs_hash_tables(flags); return init_hs_hash_tables(flags);
@ -114,8 +232,8 @@ cleanup_hs_hash_tables (void)
} }
void static void
handshake_cleanup (void) lsquic_handshake_cleanup (void)
{ {
cleanup_hs_hash_tables(); cleanup_hs_hash_tables();
lsquic_crt_cleanup(); lsquic_crt_cleanup();
@ -141,7 +259,8 @@ static int init_hs_hash_tables(int flags)
/* client */ /* 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; struct lsquic_hash_elem *el;
@ -159,7 +278,8 @@ cert_hash_item_t* c_find_certs(lsquic_str_t *domain)
/* client */ /* client */
/* certs is an array of lsquic_str_t * */ /* 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; int i;
uint64_t hash; uint64_t hash;
@ -180,7 +300,8 @@ cert_hash_item_t *make_cert_hash_item(lsquic_str_t *domain, lsquic_str_t **certs
/* client */ /* 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; int i;
if (item) if (item)
@ -196,7 +317,8 @@ void c_free_cert_hash_item(cert_hash_item_t *item)
/* client */ /* 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, if (lsquic_hash_insert(s_cached_client_certs,
lsquic_str_cstr(item->domain), 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 */ /* 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; lsquic_session_cache_info_t *entry;
struct lsquic_hash_elem *el; struct lsquic_hash_elem *el;
@ -239,7 +362,7 @@ void remove_session_info_entry(lsquic_str_t *key)
/* client */ /* client */
lsquic_session_cache_info_t * static lsquic_session_cache_info_t *
retrieve_session_info_entry (const char *key) retrieve_session_info_entry (const char *key)
{ {
lsquic_session_cache_info_t *entry; lsquic_session_cache_info_t *entry;
@ -262,7 +385,11 @@ retrieve_session_info_entry (const char *key)
/* call it in timer() */ /* 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); time_t tm = time(NULL);
struct lsquic_hash_elem *el; 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, static lsquic_enc_session_t *
const struct lsquic_engine_public *enpub) 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_session_cache_info_t *info;
lsquic_enc_session_t *enc_session; 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) if (!enc_session)
return ; 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->sstk);
lsquic_str_d(&info->scfg); 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 */ /* 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); 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; lsquic_cid_t cid;
generate_cid_buf(&cid, sizeof(cid)); generate_cid_buf(&cid, sizeof(cid));
@ -783,9 +915,9 @@ struct message_writer
#define MSG_LEN_VAL(len) (+(len)) #define MSG_LEN_VAL(len) (+(len))
int static int
gen_chlo (lsquic_enc_session_t *enc_session, enum lsquic_version version, lsquic_enc_session_gen_chlo (lsquic_enc_session_t *enc_session,
uint8_t *buf, size_t *len) enum lsquic_version version, uint8_t *buf, size_t *len)
{ {
int ret, include_pad; int ret, include_pad;
const lsquic_str_t *const ccs = get_common_certs_hash(); 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 else
ret = 0; 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; 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 uint8_t *diversification_nonce
) )
{ {
@ -1177,8 +1310,9 @@ he2str (enum handshake_error he)
* DATA_NOT_ENOUGH(-2) for not enough data, * DATA_NOT_ENOUGH(-2) for not enough data,
* DATA_FORMAT_ERROR(-1) all other errors * DATA_FORMAT_ERROR(-1) all other errors
*/ */
int handle_chlo_reply(lsquic_enc_session_t *enc_session, const uint8_t *data, static int
int len) lsquic_enc_session_handle_chlo_reply (lsquic_enc_session_t *enc_session,
const uint8_t *data, int len)
{ {
uint32_t head_tag; uint32_t head_tag;
int ret; int ret;
@ -1278,7 +1412,7 @@ int handle_chlo_reply(lsquic_enc_session_t *enc_session, const uint8_t *data,
} }
end: 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__, EV_LOG_CONN_EVENT(enc_session->cid, "%s returning %s", __func__,
he2str(ret)); he2str(ret));
return ret; return ret;
@ -1413,28 +1547,20 @@ decrypt_packet (lsquic_enc_session_t *enc_session, uint8_t path_id,
} }
#ifndef NDEBUG static int
/* In debug builds, we need to be able to override this function for testing */
__attribute__((weak))
int
lsquic_enc_session_have_key_gt_one (const lsquic_enc_session_t *enc_session) lsquic_enc_session_have_key_gt_one (const lsquic_enc_session_t *enc_session)
{ {
return enc_session && enc_session->have_key > 1; 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 /* 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. * buffer correspond to the header and the payload of incoming QUIC packet.
*/ */
/* 0 for OK, otherwise nonezero */ /* 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, uint8_t path_id, uint64_t pack_num,
unsigned char *buf, size_t *header_len, size_t data_len, unsigned char *buf, size_t *header_len, size_t data_len,
unsigned char *diversification_nonce, 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, uint8_t path_id, uint64_t pack_num,
const unsigned char *header, size_t header_len, const unsigned char *header, size_t header_len,
const unsigned char *data, size_t data_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)) && ((IS_SERVER(enc_session)) &&
enc_session->server_start_use_final_key == 0)) 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; key = enc_session->enc_ctx_i;
memcpy(nonce, enc_session->enc_key_nonce_i, 4); memcpy(nonce, enc_session->enc_key_nonce_i, 4);
if (is_shlo && enc_session->have_key == 3) 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 else
{ {
LSQ_DEBUG("lsquic_enc using 'F' key..."); LSQ_DEBUG("lsquic_enc_session_encrypt using 'F' key...");
key = enc_session->enc_ctx_f; key = enc_session->enc_ctx_f;
memcpy(nonce, enc_session->enc_key_nonce_f, 4); 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 static int
get_peer_option (const lsquic_enc_session_t *enc_session, uint32_t tag) lsquic_enc_session_get_peer_option (const lsquic_enc_session_t *enc_session,
uint32_t tag)
{ {
switch (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 /* Query a several parameters sent by the peer that are required by
* connection. * connection.
*/ */
int static int
get_peer_setting (const lsquic_enc_session_t *enc_session, uint32_t tag, lsquic_enc_session_get_peer_setting (const lsquic_enc_session_t *enc_session,
uint32_t *val) uint32_t tag, uint32_t *val)
{ {
switch (tag) 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 #if LSQUIC_KEEP_ENC_SESS_HISTORY
void static void
lsquic_get_enc_hist (const lsquic_enc_session_t *enc_session, lsquic_get_enc_hist (const lsquic_enc_session_t *enc_session,
char buf[(1 << ESHIST_BITS) + 1]) char buf[(1 << ESHIST_BITS) + 1])
{ {
@ -1635,3 +1764,25 @@ lsquic_get_enc_hist (const lsquic_enc_session_t *enc_session,
#endif #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,
};

View file

@ -1,18 +1,11 @@
/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */ /* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#ifndef LSQUIC_HANDSHAKE_H #ifndef LSQUIC_HANDSHAKE_SERVER_H
#define LSQUIC_HANDSHAKE_H #define LSQUIC_HANDSHAKE_SERVER_H
#include <stdint.h>
#include <openssl/base.h>
#include <openssl/aead.h>
#include <time.h>
#include "lsquic_str.h"
struct lsquic_engine_public; 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 STK_LENGTH 60
#define SNO_LENGTH 56 #define SNO_LENGTH 56
@ -21,16 +14,6 @@ struct sockaddr;
#define aes128_key_len 16 #define aes128_key_len 16
#define aes128_iv_len 4 #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 */ enum handshake_error /* TODO: rename this enum */
{ {
DATA_NOT_ENOUGH = -2, DATA_NOT_ENOUGH = -2,
@ -42,66 +25,6 @@ enum handshake_error /* TODO: rename this enum */
HS_2RTT = 2, 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 */ /* client side need to store 0rtt info per STK */
typedef struct lsquic_session_cache_info_st typedef struct lsquic_session_cache_info_st
{ {
@ -129,126 +52,89 @@ typedef struct lsquic_session_cache_info_st
#endif #endif
#if LSQUIC_KEEP_ENC_SESS_HISTORY #if LSQUIC_KEEP_ENC_SESS_HISTORY
#define ESHIST_BITS 7 #define ESHIST_BITS 7
#define ESHIST_MASK ((1 << ESHIST_BITS) - 1) #define ESHIST_MASK ((1 << ESHIST_BITS) - 1)
#define ESHIST_STR_SIZE ((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 #endif
typedef struct lsquic_enc_session struct enc_session_funcs
{ {
enum handshake_state hsk_state; /* Global initialization: call once per implementation */
int (*esf_global_init)(int flags);
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; /* Global cleanup: call once per implementation */
unsigned char priv_key[32]; void (*esf_global_cleanup) (void);
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 #if LSQUIC_KEEP_ENC_SESS_HISTORY
eshist_idx_t es_hist_idx; /* Grab encryption session history */
unsigned char es_hist_buf[1 << ESHIST_BITS]; void (*esf_get_hist) (const lsquic_enc_session_t *,
#endif char buf[ESHIST_STR_SIZE]);
} 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]);
#endif #endif
int handshake_init(int flags); /* Destroy enc session */
void handshake_cleanup(); void (*esf_destroy)(lsquic_enc_session_t *enc_session);
lsquic_enc_session_t * /* Return true if handshake has been completed */
new_enc_session_c(const char *domain, lsquic_cid_t cid, int (*esf_is_hsk_done)(lsquic_enc_session_t *enc_session);
const struct lsquic_engine_public *);
void free_enc_session(lsquic_enc_session_t *enc_session); /* Encrypt buffer */
void free_info(lsquic_session_cache_info_t *info); int (*esf_encrypt)(lsquic_enc_session_t *enc_session, enum lsquic_version,
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,
uint8_t path_id, uint64_t pack_num, uint8_t path_id, uint64_t pack_num,
const unsigned char *header, size_t header_len, const unsigned char *header, size_t header_len,
const unsigned char *data, size_t data_len, const unsigned char *data, size_t data_len,
unsigned char *buf_out, size_t max_out_len, size_t *out_len, unsigned char *buf_out, size_t max_out_len, size_t *out_len,
int is_hello); int is_hello);
int lsquic_dec(lsquic_enc_session_t *enc_session, enum lsquic_version, /* Decrypt buffer */
uint8_t path_id, uint64_t pack_num, int (*esf_decrypt)(lsquic_enc_session_t *enc_session, enum lsquic_version,
unsigned char *buf, size_t *header_len, size_t data_len, uint8_t path_id, uint64_t pack_num,
unsigned char *diversification_nonce, unsigned char *buf, size_t *header_len, size_t data_len,
unsigned char *buf_out, size_t max_out_len, size_t *out_len); unsigned char *diversification_nonce,
unsigned char *buf_out, size_t max_out_len, size_t *out_len);
int /* Get value of setting specified by `tag' */
get_peer_setting (const lsquic_enc_session_t *, uint32_t tag, uint32_t *val); int (*esf_get_peer_setting) (const lsquic_enc_session_t *, uint32_t tag,
uint32_t *val);
int /* Get value of peer option (that from COPT array) */
get_peer_option (const lsquic_enc_session_t *enc_session, uint32_t tag); 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 #ifdef NDEBUG
#define lsquic_enc_session_have_key_gt_one(e) ((e) && (e)->have_key > 1) const
#else
int
lsquic_enc_session_have_key_gt_one (const lsquic_enc_session_t *enc_session);
#endif #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 #endif

View file

@ -8,7 +8,7 @@
#include <stdint.h> #include <stdint.h>
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 uint64_t lsquic_packno_t;
typedef uint32_t lsquic_ver_tag_t; /* Opaque 4-byte value */ typedef uint32_t lsquic_ver_tag_t; /* Opaque 4-byte value */

View file

@ -235,8 +235,6 @@ calculate_packet_rto (lsquic_send_ctl_t *ctl)
exp = MAX_RTO_BACKOFFS; exp = MAX_RTO_BACKOFFS;
delay = delay * (1 << exp); delay = delay * (1 << exp);
if (delay > MAX_RTO_DELAY)
delay = MAX_RTO_DELAY;
return delay; return delay;
} }
@ -306,6 +304,9 @@ set_retx_alarm (lsquic_send_ctl_t *ctl)
break; break;
} }
if (delay > MAX_RTO_DELAY)
delay = MAX_RTO_DELAY;
LSQ_DEBUG("set retx alarm to %"PRIu64", which is %"PRIu64 LSQ_DEBUG("set retx alarm to %"PRIu64", which is %"PRIu64
" usec from now, mode %s", now + delay, delay, retx2str[rm]); " usec from now, mode %s", now + delay, delay, retx2str[rm]);
lsquic_alarmset_set(ctl->sc_alset, AL_RETX, now + delay); lsquic_alarmset_set(ctl->sc_alset, AL_RETX, now + delay);

View file

@ -8,17 +8,49 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h>
#include <unistd.h>
#if !(defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) && defined(__APPLE__)
#include <mach/mach_time.h>
#endif
#include "lsquic_int_types.h" #include "lsquic_int_types.h"
#include "lsquic_util.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_t
lsquic_time_now (void) 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; struct timeval tv;
(void) gettimeofday(&tv, NULL); (void) gettimeofday(&tv, NULL);
return (lsquic_time_t) tv.tv_sec * 1000000 + tv.tv_usec; return (lsquic_time_t) tv.tv_sec * 1000000 + tv.tv_usec;
#endif
} }

View file

@ -13,6 +13,9 @@ extern "C" {
lsquic_time_t lsquic_time_t
lsquic_time_now (void); lsquic_time_now (void);
void
lsquic_init_timers (void);
/* Returns 1 if `buf' contains only zero bytes, 0 otherwise. /* Returns 1 if `buf' contains only zero bytes, 0 otherwise.
*/ */
int int