litespeed-quic/src/liblsquic/lsquic_handshake.c

1638 lines
50 KiB
C

/* Copyright (c) 2017 LiteSpeed Technologies Inc. See LICENSE. */
#include <assert.h>
#include <errno.h>
#include <time.h>
#include <string.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <openssl/ssl.h>
#include <openssl/crypto.h>
#include <openssl/stack.h>
#include <openssl/x509.h>
#include <openssl/rand.h>
#include <openssl/nid.h>
#include <zlib.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_crypto.h"
#include "lsquic_handshake.h"
#include "lsquic_parse.h"
#include "lsquic_crt_compress.h"
#include "lsquic_util.h"
#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 "fiu-local.h"
#include "lsquic_ev_log.h"
#define MIN_CHLO_SIZE 1024
#define LSQUIC_LOGGER_MODULE LSQLM_HANDSHAKE
#include "lsquic_logger.h"
/***
* client side, it will store the domain/certs as cache cert
*/
static struct lsquic_hash *s_cached_client_certs;
/**
* client side will save the session_info for next time 0rtt
*/
static struct lsquic_hash *s_cached_client_session_infos;
static int get_tag_val_u32 (unsigned char *v, int len, uint32_t *val);
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 int determine_keys(lsquic_enc_session_t *enc_session);
#if LSQUIC_KEEP_ENC_SESS_HISTORY
static void
eshist_append (lsquic_enc_session_t *enc_session,
enum enc_sess_history_event eh_event)
{
enc_session->es_hist_buf[
ESHIST_MASK & enc_session->es_hist_idx++ ] = eh_event;
}
# define ESHIST_APPEND(sess, event) eshist_append(sess, event)
#else
# define ESHIST_APPEND(sess, event) do { } while (0)
#endif
int
handshake_init(int flags)
{
crypto_init();
return init_hs_hash_tables(flags);
}
static void
cleanup_hs_hash_tables (void)
{
struct lsquic_hash_elem *el;
if (s_cached_client_session_infos)
{
for (el = lsquic_hash_first(s_cached_client_session_infos); el;
el = lsquic_hash_next(s_cached_client_session_infos))
{
lsquic_session_cache_info_t *entry = lsquic_hashelem_getdata(el);
free_info(entry);
}
lsquic_hash_destroy(s_cached_client_session_infos);
s_cached_client_session_infos = NULL;
}
if (s_cached_client_certs)
{
for (el = lsquic_hash_first(s_cached_client_certs); el;
el = lsquic_hash_next(s_cached_client_certs))
{
cert_hash_item_t *item = lsquic_hashelem_getdata(el);
c_free_cert_hash_item(item);
}
lsquic_hash_destroy(s_cached_client_certs);
s_cached_client_certs = NULL;
}
}
void
handshake_cleanup (void)
{
cleanup_hs_hash_tables();
lsquic_crt_cleanup();
}
/* return -1 for fail, 0 OK*/
static int init_hs_hash_tables(int flags)
{
if (flags & LSQUIC_GLOBAL_CLIENT)
{
s_cached_client_session_infos = lsquic_hash_create();
if (!s_cached_client_session_infos)
return -1;
s_cached_client_certs = lsquic_hash_create();
if (!s_cached_client_certs)
return -1;
}
return 0;
}
/* client */
cert_hash_item_t* c_find_certs(lsquic_str_t *domain)
{
struct lsquic_hash_elem *el;
if (!s_cached_client_certs)
return NULL;
el = lsquic_hash_find(s_cached_client_certs, lsquic_str_cstr(domain),
lsquic_str_len(domain));
if (el == NULL)
return NULL;
return lsquic_hashelem_getdata(el);
}
/* 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)
{
int i;
uint64_t hash;
cert_hash_item_t *item = (cert_hash_item_t *)malloc(sizeof(cert_hash_item_t));
item->crts = (lsquic_str_t *)malloc(count * sizeof(lsquic_str_t));
item->domain = lsquic_str_new(NULL, 0);
item->hashs = lsquic_str_new(NULL, 0);
lsquic_str_copy(item->domain, domain);
item->count = count;
for(i=0; i<count; ++i)
{
lsquic_str_copy(&item->crts[i], certs[i]);
hash = fnv1a_64((const uint8_t *)lsquic_str_cstr(certs[i]), lsquic_str_len(certs[i]));
lsquic_str_append(item->hashs, (char *)&hash, 8);
}
return item;
}
/* client */
void c_free_cert_hash_item(cert_hash_item_t *item)
{
int i;
if (item)
{
lsquic_str_delete(item->hashs);
lsquic_str_delete(item->domain);
for(i=0; i<item->count; ++i)
lsquic_str_d(&item->crts[i]);
free(item->crts);
free(item);
}
}
/* client */
int c_insert_certs(cert_hash_item_t *item)
{
if (lsquic_hash_insert(s_cached_client_certs,
lsquic_str_cstr(item->domain),
lsquic_str_len(item->domain), item) == NULL)
return -1;
else
return 0;
}
static int save_session_info_entry(lsquic_str_t *key, lsquic_session_cache_info_t *entry)
{
lsquic_str_setto(&entry->sni_key, lsquic_str_cstr(key), lsquic_str_len(key));
if (lsquic_hash_insert(s_cached_client_session_infos,
lsquic_str_cstr(&entry->sni_key),
lsquic_str_len(&entry->sni_key), entry) == NULL)
{
lsquic_str_d(&entry->sni_key);
return -1;
}
else
return 0;
}
/* If entry updated and need to remove cached entry */
void remove_session_info_entry(lsquic_str_t *key)
{
lsquic_session_cache_info_t *entry;
struct lsquic_hash_elem *el;
el = lsquic_hash_find(s_cached_client_session_infos,
lsquic_str_cstr(key), lsquic_str_len(key));
if (el)
{
entry = lsquic_hashelem_getdata(el);
lsquic_str_d(&entry->sni_key);
lsquic_hash_erase(s_cached_client_session_infos, el);
}
}
/* client */
lsquic_session_cache_info_t *
retrieve_session_info_entry (const char *key)
{
lsquic_session_cache_info_t *entry;
struct lsquic_hash_elem *el;
if (!s_cached_client_session_infos)
return NULL;
if (!key)
return NULL;
el = lsquic_hash_find(s_cached_client_session_infos, key, strlen(key));
if (el == NULL)
return NULL;
entry = lsquic_hashelem_getdata(el);
LSQ_DEBUG("[QUIC]retrieve_session_info_entry find cached session info %p.\n", entry);
return entry;
}
/* call it in timer() */
void remove_expire_session_info_entry()
{
time_t tm = time(NULL);
struct lsquic_hash_elem *el;
for (el = lsquic_hash_first(s_cached_client_session_infos); el;
el = lsquic_hash_next(s_cached_client_session_infos))
{
lsquic_session_cache_info_t *entry = lsquic_hashelem_getdata(el);
if ((uint64_t)tm > entry->expy)
{
free_info(entry);
lsquic_hash_erase(s_cached_client_session_infos, el);
}
}
}
lsquic_enc_session_t *new_enc_session_c(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;
if (!domain)
{
errno = EINVAL;
return NULL;
}
enc_session = calloc(1, sizeof(*enc_session));
if (!enc_session)
return NULL;
info = retrieve_session_info_entry(domain);
if (info)
memcpy(enc_session->hs_ctx.pubs, info->spubs, 32);
else
{
info = calloc(1, sizeof(*info));
if (!info)
{
free(enc_session);
return NULL;
}
}
enc_session->enpub = enpub;
enc_session->cid = cid;
enc_session->info = info;
/* FIXME: allocation may fail */
lsquic_str_append(&enc_session->hs_ctx.sni, domain, strlen(domain));
return enc_session;
}
void free_enc_session(lsquic_enc_session_t *enc_session)
{
if (!enc_session)
return ;
hs_ctx_t *hs_ctx = &enc_session->hs_ctx;
lsquic_str_d(&hs_ctx->sni);
lsquic_str_d(&hs_ctx->ccs);
lsquic_str_d(&hs_ctx->ccrt);
lsquic_str_d(&hs_ctx->stk);
lsquic_str_d(&hs_ctx->sno);
lsquic_str_d(&hs_ctx->prof);
lsquic_str_d(&hs_ctx->csct);
lsquic_str_d(&hs_ctx->crt);
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);
}
free(enc_session);
}
void free_info(lsquic_session_cache_info_t *info)
{
lsquic_str_d(&info->sstk);
lsquic_str_d(&info->scfg);
lsquic_str_d(&info->sni_key);
free(info);
}
static int get_hs_state(lsquic_enc_session_t *enc_session)
{
return enc_session->hsk_state;
}
/* make sure have more room for encrypt */
int is_hs_done(lsquic_enc_session_t *enc_session)
{
return (get_hs_state(enc_session) == HSK_COMPLETED);
}
static void
process_copt (lsquic_enc_session_t *enc_session, const uint32_t *const opts,
unsigned n_opts)
{
unsigned i;
for (i = 0; i < n_opts; ++i)
switch (opts[i])
{
case QTAG_NSTP:
enc_session->hs_ctx.opts |= HOPT_NSTP;
break;
case QTAG_SREJ:
enc_session->hs_ctx.opts |= HOPT_SREJ;
break;
}
}
static int parse_hs_data (lsquic_enc_session_t *enc_session, uint32_t tag,
unsigned char *val, int len, uint32_t head_tag)
{
hs_ctx_t * hs_ctx = &enc_session->hs_ctx;
switch(tag)
{
case QTAG_PDMD:
hs_ctx->pdmd = get_tag_value_i32(val, len);
break;
case QTAG_MIDS:
if (0 != get_tag_val_u32(val, len, &hs_ctx->mids))
return -1;
break;
case QTAG_SCLS:
hs_ctx->scls = get_tag_value_i32(val, len);
break;
case QTAG_CFCW:
if (0 != get_tag_val_u32(val, len, &hs_ctx->cfcw))
return -1;
break;
case QTAG_SFCW:
if (0 != get_tag_val_u32(val, len, &hs_ctx->sfcw))
return -1;
break;
case QTAG_SRBF:
hs_ctx->srbf = get_tag_value_i32(val, len);
break;
case QTAG_ICSL:
hs_ctx->icsl = get_tag_value_i32(val, len);
break;
case QTAG_IRTT:
hs_ctx->irtt = get_tag_value_i32(val, len);
break;
case QTAG_COPT:
if (0 == len % sizeof(uint32_t))
process_copt(enc_session, (uint32_t *) val, len / sizeof(uint32_t));
/* else ignore, following the reference implementation */
break;
case QTAG_CTIM:
hs_ctx->ctim = get_tag_value_i64(val, len);
break;
case QTAG_SNI:
lsquic_str_setto(&hs_ctx->sni, val, len);
ESHIST_APPEND(enc_session, ESHE_SET_SNI);
break;
case QTAG_CCS:
lsquic_str_setto(&hs_ctx->ccs, val, len);
break;
case QTAG_CCRT:
lsquic_str_setto(&hs_ctx->ccrt, val, len);
break;
case QTAG_CRT:
lsquic_str_setto(&hs_ctx->crt, val, len);
break;
case QTAG_PUBS:
/* FIXME:Server side may send a list of pubs,
* we support only ONE kenx now.
* REJ is 35 bytes, SHLO is 32 bytes
* Only save other peer's pubs to hs_ctx
*/
if( len < 32)
break;
memcpy(hs_ctx->pubs, val + (len - 32), 32);
if (head_tag == QTAG_SCFG)
{
memcpy(enc_session->info->spubs, hs_ctx->pubs, 32);
}
break;
case QTAG_RCID:
hs_ctx->rcid = get_tag_value_i64(val, len);
break;
case QTAG_SMHL:
if (0 != get_tag_val_u32(val, len, &hs_ctx->smhl))
return -1;
hs_ctx->set |= HSET_SMHL;
break;
case QTAG_TCID:
if (0 != get_tag_val_u32(val, len, &hs_ctx->tcid))
return -1;
hs_ctx->set |= HSET_TCID;
break;
case QTAG_EXPY:
enc_session->info->expy = get_tag_value_i64(val, len);
break;
case QTAG_ORBT:
enc_session->info->orbt = get_tag_value_i64(val, len);
break;
case QTAG_SNO:
lsquic_str_setto(&enc_session->ssno, val, len);
ESHIST_APPEND(enc_session, ESHE_SET_SNO);
break;
case QTAG_STK:
if (lsquic_str_len(&enc_session->info->sstk) > 0)
remove_session_info_entry(&enc_session->info->sstk);
lsquic_str_setto(&enc_session->info->sstk, val, len);
ESHIST_APPEND(enc_session, ESHE_SET_STK);
break;
case QTAG_SCID:
if (len != SCID_LENGTH)
return -1;
memcpy(enc_session->info->sscid, val, len);
ESHIST_APPEND(enc_session, ESHE_SET_SCID);
break;
case QTAG_AEAD:
enc_session->info->aead = get_tag_value_i32(val, len);
break;
case QTAG_KEXS:
enc_session->info->kexs = get_tag_value_i32(val, len);
break;
case QTAG_NONC:
if (len != sizeof(hs_ctx->nonc))
return -1;
memcpy(hs_ctx->nonc, val, len);
break;
case QTAG_SCFG:
lsquic_str_setto(&enc_session->info->scfg, val, len);
enc_session->info->scfg_flag = 1;
break;
case QTAG_PROF:
lsquic_str_setto(&hs_ctx->prof, val, len);
ESHIST_APPEND(enc_session, ESHE_SET_PROF);
break;
case QTAG_STTL:
hs_ctx->sttl = get_tag_value_i64(val, len);
break;
default:
LSQ_DEBUG("Ignored tag '%.*s'", 4, (char *)&tag);
break;
}
return 0;
}
/* only for the hs stream-frame data, NOT with the packet header or frame header*/
static enum handshake_error parse_hs (lsquic_enc_session_t *enc_session,
const unsigned char *buf, int buf_len,
uint32_t *head_tag)
{
uint16_t i;
const unsigned char *p = buf;
const unsigned char *pend = buf + buf_len;
unsigned char *data;
uint32_t len = 0, offset = 0;
uint16_t num;
uint32_t tag;
if (buf_len < 6)
return DATA_FORMAT_ERROR;
memcpy(&tag, p, 4);
p += 4;
{
if (tag != QTAG_SREJ && tag != QTAG_REJ && tag != QTAG_SHLO &&
tag != QTAG_SCFG)
return DATA_FORMAT_ERROR;
}
*head_tag = tag;
memcpy((char *)&num, p, 2);
p += 2 + 2; /* the 2 bytes padding 0x0000 need to be bypassed */
if (num < 1)
return DATA_FORMAT_ERROR;
data = (uint8_t *)(buf + 4 * 2 * (1 + num));
if ((const char *)data > (const char *)pend)
{
LSQ_DEBUG("parse_hs tag '%.*s' error: data not enough", 4, (char *)head_tag);
return DATA_NOT_ENOUGH;
}
/* check last offset */
memcpy((char *)&len, data - 4, 4);
if ((const char *)data + len > (const char *)pend)
{
LSQ_DEBUG("parse_hs tag '%.*s' error: data not enough!!!", 4, (char *)head_tag);
return DATA_NOT_ENOUGH;
}
for (i=0; i<num; ++i)
{
memcpy((char *)&tag, p, 4);
p += 4;
memcpy((char *)&len, p, 4);
len -= offset;
p += 4;
if ((const char *)data + offset + len > (const char *)pend)
return DATA_FORMAT_ERROR;
if (0 != parse_hs_data(enc_session, tag, data + offset, len,
*head_tag))
return DATA_FORMAT_ERROR;
offset += len;
}
LSQ_DEBUG("parse_hs tag '%.*s' no error.", 4, (char *)head_tag);
return DATA_NO_ERROR;
}
static uint32_t get_tag_value_i32(unsigned char *val, int len)
{
uint32_t v;
if (len < 4)
return 0;
memcpy(&v, val, 4);
return v;
}
static uint64_t get_tag_value_i64(unsigned char *val, int len)
{
uint64_t v;
if (len < 8)
return 0;
memcpy(&v, val, 8);
return v;
}
static int
get_tag_val_u32 (unsigned char *v, int len, uint32_t *val)
{
if (len != 4)
return -1;
memcpy(val, v, 4);
return 0;
}
static void
generate_cid_buf (void *buf, size_t bufsz)
{
RAND_bytes(buf, bufsz);
}
lsquic_cid_t generate_cid(void)
{
lsquic_cid_t cid;
generate_cid_buf(&cid, sizeof(cid));
return cid;
}
/* From "QUIC Crypto" for easy reference:
*
* A handshake message consists of:
* - The tag of the message.
* - A uint16 containing the number of tag-value pairs.
* - Two bytes of padding which should be zero when sent but ignored when
* received.
* - A series of uint32 tags and uint32 end offsets, one for each
* tag-value pair. The tags must be strictly monotonically
* increasing, and the end-offsets must be monotonic non-decreasing.
* The end offset gives the offset, from the start of the value
* data, to a byte one beyond the end of the data for that tag.
* (Thus the end offset of the last tag contains the length of the
* value data).
* - The value data, concatenated without padding.
*/
struct table_entry { uint32_t tag, off; };
struct message_writer
{
unsigned char *mw_p;
struct table_entry mw_first_dummy_entry;
struct table_entry *mw_entry,
*mw_prev_entry,
*mw_end;
};
/* MW_ family of macros is used to write entries to handshake message
* (MW stands for "message writer").
*/
#define MW_BEGIN(mw, msg_tag, n_entries, data_ptr) do { \
uint32_t t_ = msg_tag; \
uint16_t n_ = n_entries; \
memcpy(data_ptr, &t_, 4); \
memcpy(data_ptr + 4, &n_, 2); \
memset(data_ptr + 4 + 2, 0, 2); \
(mw)->mw_entry = (void *) (data_ptr + 8); \
(mw)->mw_p = data_ptr + 8 + \
n_entries * sizeof((mw)->mw_entry[0]); \
(mw)->mw_first_dummy_entry.tag = 0; \
(mw)->mw_first_dummy_entry.off = 0; \
(mw)->mw_prev_entry = &(mw)->mw_first_dummy_entry; \
(mw)->mw_end = (void *) (mw)->mw_p; \
} while (0)
#ifndef NDEBUG
# define MW_END(mw) do { \
assert((mw)->mw_entry == (mw)->mw_end); \
} while (0)
#else
# define MW_END(mw)
#endif
#define MW_P(mw) ((mw)->mw_p)
#define MW_ADVANCE_P(mw, n) do { \
MW_P(mw) += (n); \
} while (0)
#define MW_WRITE_TABLE_ENTRY(mw, tag_, sz) do { \
assert((mw)->mw_prev_entry->tag < (tag_)); \
assert((mw)->mw_entry < (mw)->mw_end); \
(mw)->mw_entry->tag = (tag_); \
(mw)->mw_entry->off = (mw)->mw_prev_entry->off + (sz); \
(mw)->mw_prev_entry = (mw)->mw_entry; \
++(mw)->mw_entry; \
} while (0)
#define MW_WRITE_BUFFER(mw, tag, buf, sz) do { \
MW_WRITE_TABLE_ENTRY(mw, tag, sz); \
memcpy(MW_P(mw), buf, sz); \
MW_ADVANCE_P(mw, sz); \
} while (0)
#define MW_WRITE_LS_STR(mw, tag, s) \
MW_WRITE_BUFFER(mw, tag, lsquic_str_buf(s), lsquic_str_len(s))
#define MW_WRITE_UINT32(mw, tag, val) do { \
uint32_t v_ = (val); \
MW_WRITE_BUFFER(mw, tag, &v_, sizeof(v_)); \
} while (0)
#define MW_WRITE_UINT64(mw, tag, val) do { \
uint64_t v_ = (val); \
MW_WRITE_BUFFER(mw, tag, &v_, sizeof(v_)); \
} while (0)
/* MSG_LEN_ family of macros calculates buffer size required for a
* handshake message.
*/
#define MSG_LEN_INIT(len) do { \
len = 4 /* Tag */ + 2 /* # tags */ + 2 /* Two zero bytes */; \
} while (0)
#define MSG_LEN_ADD(len, payload_sz) do { \
len += 4 + 4 + (payload_sz); \
} while (0)
#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)
{
int ret, include_pad;
const lsquic_str_t *const ccs = get_common_certs_hash();
const struct lsquic_engine_settings *const settings =
&enc_session->enpub->enp_settings;
cert_hash_item_t *const cached_certs_item =
c_find_certs(&enc_session->hs_ctx.sni);
unsigned char pub_key[32];
size_t ua_len;
uint32_t opts[1]; /* Only NSTP is supported for now */
unsigned n_opts, msg_len, n_tags, pad_size;
struct message_writer mw;
/* Before we do anything else, sanity check: */
if (*len < MIN_CHLO_SIZE)
return -1;
n_opts = 0;
if (settings->es_support_nstp)
opts[ n_opts++ ] = QTAG_NSTP;
/* Count tags and calculate required buffer size: */
MSG_LEN_INIT(msg_len); n_tags = 0;
MSG_LEN_ADD(msg_len, 4); ++n_tags; /* PDMD */
MSG_LEN_ADD(msg_len, 4); ++n_tags; /* AEAD */
MSG_LEN_ADD(msg_len, 4); ++n_tags; /* VER */
MSG_LEN_ADD(msg_len, 4); ++n_tags; /* MIDS */
MSG_LEN_ADD(msg_len, 4); ++n_tags; /* SCLS */
MSG_LEN_ADD(msg_len, 4); ++n_tags; /* CFCW */
MSG_LEN_ADD(msg_len, 4); ++n_tags; /* SFCW */
MSG_LEN_ADD(msg_len, 4); ++n_tags; /* ICSL */
MSG_LEN_ADD(msg_len, 4); ++n_tags; /* SMHL */
MSG_LEN_ADD(msg_len, 8); ++n_tags; /* CTIM */
MSG_LEN_ADD(msg_len, 4); ++n_tags; /* KEXS */
MSG_LEN_ADD(msg_len, 0); ++n_tags; /* CSCT */
if (n_opts > 0)
{
MSG_LEN_ADD(msg_len, sizeof(opts[0]) * n_opts);
++n_tags; /* COPT */
}
if (settings->es_ua)
{
ua_len = strlen(settings->es_ua);
if (ua_len > 0)
{
MSG_LEN_ADD(msg_len, ua_len); ++n_tags; /* UAID */
}
}
else
ua_len = 0;
MSG_LEN_ADD(msg_len, lsquic_str_len(&enc_session->hs_ctx.sni));
++n_tags; /* SNI */
MSG_LEN_ADD(msg_len, lsquic_str_len(ccs)); ++n_tags; /* CCS */
if (cached_certs_item)
{
enc_session->cert_ptr = &cached_certs_item->crts[0];
MSG_LEN_ADD(msg_len, lsquic_str_len(cached_certs_item->hashs));
++n_tags; /* CCRT */
MSG_LEN_ADD(msg_len, 8); ++n_tags; /* XLCT */
}
MSG_LEN_ADD(msg_len, lsquic_str_len(&enc_session->ssno));
++n_tags; /* SNO */
MSG_LEN_ADD(msg_len, lsquic_str_len(&enc_session->info->sstk));
++n_tags; /* STK */
if (lsquic_str_len(&enc_session->info->scfg) > 0)
{
MSG_LEN_ADD(msg_len, sizeof(enc_session->info->sscid));
++n_tags; /* SCID */
if (enc_session->cert_ptr)
{
MSG_LEN_ADD(msg_len, sizeof(pub_key));
++n_tags; /* PUBS */
MSG_LEN_ADD(msg_len, sizeof(enc_session->hs_ctx.nonc));
++n_tags; /* NONC */
rand_bytes(enc_session->priv_key, 32);
c255_get_pub_key(enc_session->priv_key, pub_key);
gen_nonce_c(enc_session->hs_ctx.nonc, enc_session->info->orbt);
}
}
include_pad = MSG_LEN_VAL(msg_len) < MIN_CHLO_SIZE;
if (include_pad)
{
if (MSG_LEN_VAL(msg_len) + sizeof(struct table_entry) < MIN_CHLO_SIZE)
pad_size = MIN_CHLO_SIZE - MSG_LEN_VAL(msg_len) -
sizeof(struct table_entry);
else
pad_size = 0;
MSG_LEN_ADD(msg_len, pad_size); ++n_tags; /* PAD */
}
/* Check that we have enough room in the output buffer: */
if (MSG_LEN_VAL(msg_len) > *len)
return -1;
/* Calculate any remaining values: */
enc_session->hs_ctx.ctim = time(NULL);
/* XXX: should we use MSPC instead of MIDS in newer versions of gQUIC? */
/* Write CHLO: */
MW_BEGIN(&mw, QTAG_CHLO, n_tags, buf);
if (include_pad)
{
memset(MW_P(&mw), '-', pad_size);
MW_WRITE_TABLE_ENTRY(&mw, QTAG_PAD, pad_size);
MW_ADVANCE_P(&mw, pad_size);
}
MW_WRITE_LS_STR(&mw, QTAG_SNI, &enc_session->hs_ctx.sni);
MW_WRITE_LS_STR(&mw, QTAG_STK, &enc_session->info->sstk);
MW_WRITE_LS_STR(&mw, QTAG_SNO, &enc_session->ssno);
MW_WRITE_UINT32(&mw, QTAG_VER, lsquic_ver2tag(version));
MW_WRITE_LS_STR(&mw, QTAG_CCS, ccs);
if (lsquic_str_len(&enc_session->info->scfg) > 0 && enc_session->cert_ptr)
MW_WRITE_BUFFER(&mw, QTAG_NONC, enc_session->hs_ctx.nonc,
sizeof(enc_session->hs_ctx.nonc));
MW_WRITE_UINT32(&mw, QTAG_AEAD, settings->es_aead);
if (ua_len)
MW_WRITE_BUFFER(&mw, QTAG_UAID, settings->es_ua, ua_len);
if (lsquic_str_len(&enc_session->info->scfg) > 0)
MW_WRITE_BUFFER(&mw, QTAG_SCID, enc_session->info->sscid,
sizeof(enc_session->info->sscid));
MW_WRITE_UINT32(&mw, QTAG_PDMD, settings->es_pdmd);
MW_WRITE_UINT32(&mw, QTAG_SMHL, 1);
MW_WRITE_UINT32(&mw, QTAG_ICSL, settings->es_idle_conn_to / 1000000);
MW_WRITE_UINT64(&mw, QTAG_CTIM, enc_session->hs_ctx.ctim);
if (lsquic_str_len(&enc_session->info->scfg) > 0 && enc_session->cert_ptr)
MW_WRITE_BUFFER(&mw, QTAG_PUBS, pub_key, sizeof(pub_key));
MW_WRITE_UINT32(&mw, QTAG_MIDS, settings->es_max_streams_in);
MW_WRITE_UINT32(&mw, QTAG_SCLS, settings->es_silent_close);
MW_WRITE_UINT32(&mw, QTAG_KEXS, settings->es_kexs);
if (cached_certs_item)
MW_WRITE_BUFFER(&mw, QTAG_XLCT, lsquic_str_buf(cached_certs_item->hashs), 8);
/* CSCT is empty on purpose (retained from original code) */
MW_WRITE_TABLE_ENTRY(&mw, QTAG_CSCT, 0);
if (n_opts > 0)
MW_WRITE_BUFFER(&mw, QTAG_COPT, opts, n_opts * sizeof(opts[0]));
if (cached_certs_item)
MW_WRITE_LS_STR(&mw, QTAG_CCRT, cached_certs_item->hashs);
MW_WRITE_UINT32(&mw, QTAG_CFCW, settings->es_cfcw);
MW_WRITE_UINT32(&mw, QTAG_SFCW, settings->es_sfcw);
MW_END(&mw);
assert(buf + *len >= MW_P(&mw));
*len = MW_P(&mw) - buf;
lsquic_str_setto(&enc_session->chlo, buf, *len);
if (lsquic_str_len(&enc_session->info->scfg) > 0 && enc_session->cert_ptr)
{
enc_session->have_key = 0;
assert(lsquic_str_len(enc_session->cert_ptr) > 0);
ret = determine_keys(enc_session
);
enc_session->have_key = 1;
}
else
ret = 0;
LSQ_DEBUG("gen_chlo called, return %d, buf_len %zd.", ret, *len);
return ret;
}
static int handle_chlo_reply_verify_prof(lsquic_enc_session_t *enc_session,
lsquic_str_t **out_certs,
size_t *out_certs_count,
lsquic_str_t *cached_certs,
int cached_certs_count)
{
const unsigned char *const in =
(const unsigned char *) lsquic_str_buf(&enc_session->hs_ctx.crt);
const unsigned char *const in_end =
in + lsquic_str_len(&enc_session->hs_ctx.crt);
EVP_PKEY *pub_key;
int ret;
X509 *cert;
ret = decompress_certs(in, in_end,cached_certs, cached_certs_count,
out_certs, out_certs_count);
if (ret)
return ret;
cert = bio_to_crt((const char *)lsquic_str_cstr(out_certs[0]),
lsquic_str_len(out_certs[0]), 0);
pub_key = X509_get_pubkey(cert);
ret = verify_prof((const uint8_t *)lsquic_str_cstr(&enc_session->chlo),
(size_t)lsquic_str_len(&enc_session->chlo),
&enc_session->info->scfg,
pub_key,
(const uint8_t *)lsquic_str_cstr(&enc_session->hs_ctx.prof),
lsquic_str_len(&enc_session->hs_ctx.prof));
EVP_PKEY_free(pub_key);
X509_free(cert);
return ret;
}
void setup_aead_ctx(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;
if (*ctx)
{
EVP_AEAD_CTX_cleanup(*ctx);
}
else
*ctx = (EVP_AEAD_CTX *)malloc(sizeof(EVP_AEAD_CTX));
EVP_AEAD_CTX_init(*ctx, aead_, key, key_len, auth_tag_size, NULL);
if (key_copy)
memcpy(key_copy, key, key_len);
}
int determine_diversification_key(lsquic_enc_session_t *enc_session,
uint8_t *diversification_nonce
)
{
EVP_AEAD_CTX **ctx_s_key;
unsigned char *key_i, *iv;
uint8_t ikm[aes128_key_len + aes128_iv_len];
ctx_s_key = &enc_session->dec_ctx_i;
key_i = enc_session->dec_key_i;
iv = enc_session->dec_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,
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);
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));
return 0;
}
/* After CHLO msg generatered, call it to determine_keys */
static int determine_keys(lsquic_enc_session_t *enc_session)
{
lsquic_str_t *chlo = &enc_session->chlo;
uint8_t shared_key_c[32];
struct lsquic_buf *nonce_c = lsquic_buf_create(100);
struct lsquic_buf *hkdf_input = lsquic_buf_create(0);
unsigned char c_key[aes128_key_len];
unsigned char s_key[aes128_key_len];
unsigned char *c_key_bin = NULL;
unsigned char *s_key_bin = NULL;
unsigned char *c_iv;
unsigned char *s_iv;
unsigned char sub_key[32];
EVP_AEAD_CTX **ctx_c_key, **ctx_s_key;
char key_flag;
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 */
key_flag = 'I';
}
else
{
lsquic_buf_append(hkdf_input, "QUIC forward secure key expansion\0", 33 + 1); // Add a 0x00 */
key_flag = 'F';
}
c255_gen_share_key(enc_session->priv_key,
enc_session->hs_ctx.pubs,
(unsigned char *)shared_key_c);
{
if (enc_session->have_key == 0)
{
ctx_c_key = &enc_session->enc_ctx_i;
ctx_s_key = &enc_session->dec_ctx_i;
c_iv = (unsigned char *) enc_session->enc_key_nonce_i;
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;
}
else
{
ctx_c_key = &enc_session->enc_ctx_f;
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;
}
}
LSQ_DEBUG("export_key_material c255_gen_share_key %s",
get_bin_str(shared_key_c, 32, 512));
lsquic_buf_append(hkdf_input, (char *)&enc_session->cid, sizeof(enc_session->cid));
lsquic_buf_append(hkdf_input, lsquic_str_cstr(chlo), lsquic_str_len(chlo)); /* CHLO msg */
{
lsquic_buf_append(hkdf_input, lsquic_str_cstr(&enc_session->info->scfg),
lsquic_str_len(&enc_session->info->scfg)); /* scfg msg */
}
lsquic_buf_append(hkdf_input, lsquic_str_cstr(enc_session->cert_ptr),
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));
/* 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));
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),
aes128_key_len, c_key,
aes128_key_len, s_key,
aes128_iv_len, c_iv,
aes128_iv_len, s_iv,
sub_key);
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);
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));
return 0;
}
/* 0 Match */
static int cached_certs_match(cert_hash_item_t *cached_certs_item, lsquic_str_t **certs,
int certs_count)
{
int i;
if (!cached_certs_item || cached_certs_item->count != certs_count)
return -1;
for (i=0; i<certs_count; ++i)
{
if (lsquic_str_bcmp(certs[i], &cached_certs_item->crts[i]) != 0)
return -1;
}
return 0;
}
static const char *
he2str (enum handshake_error he)
{
switch (he)
{
case DATA_NOT_ENOUGH: return "DATA_NOT_ENOUGH";
case HS_ERROR: return "HS_ERROR";
case HS_SHLO: return "HS_SHLO";
case HS_1RTT: return "HS_1RTT";
case HS_2RTT: return "HS_2RTT";
default:
assert(0); return "<unknown enum value>";
}
}
/* NOT packet, just the frames-data */
/* return rtt number:
* 0 OK
* 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)
{
uint32_t head_tag;
int ret;
lsquic_session_cache_info_t *info = enc_session->info;
hs_ctx_t * hs_ctx = &enc_session->hs_ctx;
cert_hash_item_t *cached_certs_item = c_find_certs(&hs_ctx->sni);
/* FIXME get the number first */
lsquic_str_t **out_certs = NULL;
size_t out_certs_count = 0, i;
ret = parse_hs(enc_session, data, len, &head_tag);
if (ret)
goto end;
if (head_tag != QTAG_SREJ &&
head_tag != QTAG_REJ &&
head_tag != QTAG_SHLO)
{
ret = 1;
goto end;
}
if (head_tag == QTAG_SREJ || head_tag == QTAG_REJ)
enc_session->hsk_state = HSK_CHLO_REJ;
else if(head_tag == QTAG_SHLO)
{
enc_session->hsk_state = HSK_COMPLETED;
}
if (info->scfg_flag == 1)
{
ret = parse_hs(enc_session, (uint8_t *)lsquic_str_cstr(&info->scfg),
lsquic_str_len(&info->scfg), &head_tag);
/* After handled, set the length to 0 to avoid do it again*/
enc_session->info->scfg_flag = 2;
if (ret)
goto end;
if (lsquic_str_len(&enc_session->hs_ctx.crt) > 0)
{
out_certs_count = get_certs_count(&enc_session->hs_ctx.crt);
if (out_certs_count > 0)
{
out_certs = (lsquic_str_t **)malloc(out_certs_count * sizeof(lsquic_str_t *));
if (!out_certs)
{
ret = -1;
goto end;
}
for (i=0; i<out_certs_count; ++i)
out_certs[i] = lsquic_str_new(NULL, 0);
ret = handle_chlo_reply_verify_prof(enc_session, out_certs,
&out_certs_count,
(cached_certs_item ? cached_certs_item->crts : NULL),
(cached_certs_item ? cached_certs_item->count : 0));
if (ret == 0)
{
if (out_certs_count > 0)
{
if (cached_certs_item &&
cached_certs_match(cached_certs_item, out_certs, out_certs_count) == 0)
;
else
{
if (cached_certs_item)
c_free_cert_hash_item(cached_certs_item);
cached_certs_item = make_cert_hash_item(&hs_ctx->sni,
out_certs, out_certs_count);
c_insert_certs(cached_certs_item);
}
enc_session->cert_ptr = &cached_certs_item->crts[0];
}
}
for (i=0; i<out_certs_count; ++i)
lsquic_str_delete(out_certs[i]);
free(out_certs);
if (ret)
goto end;
}
}
}
if (enc_session->hsk_state == HSK_COMPLETED)
{
if (!lsquic_str_buf(&info->sni_key))
save_session_info_entry(&enc_session->hs_ctx.sni, info);
ret = determine_keys(enc_session
); /* FIXME: check ret */
enc_session->have_key = 3;
}
end:
LSQ_DEBUG("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;
}
static uint64_t combine_path_id_pack_num(uint8_t path_id, uint64_t pack_num)
{
uint64_t v = ((uint64_t)path_id << 56) | pack_num;
return v;
}
# define IS_SERVER(session) 0
static int
verify_packet_hash (const lsquic_enc_session_t *enc_session,
enum lsquic_version version, const unsigned char *buf, size_t *header_len,
size_t data_len, unsigned char *buf_out, size_t max_out_len,
size_t *out_len)
{
uint8_t md[HS_PKT_HASH_LENGTH];
uint128 hash;
int ret;
if (data_len < HS_PKT_HASH_LENGTH)
return -1;
if (version >= LSQVER_037)
{
hash = fnv1a_128_3(buf, *header_len,
buf + *header_len + HS_PKT_HASH_LENGTH,
data_len - HS_PKT_HASH_LENGTH,
(unsigned char *) "Server", 6);
}
else
{
hash = fnv1a_128_2(buf, *header_len,
buf + *header_len + HS_PKT_HASH_LENGTH,
data_len - HS_PKT_HASH_LENGTH);
}
serialize_fnv128_short(hash, md);
ret = memcmp(md, buf + *header_len, HS_PKT_HASH_LENGTH);
if(ret == 0)
{
*header_len += HS_PKT_HASH_LENGTH;
*out_len = data_len - HS_PKT_HASH_LENGTH;
if (max_out_len < *header_len + *out_len)
return -1;
memcpy(buf_out, buf, *header_len + *out_len);
return 0;
}
else
return -1;
}
static int
decrypt_packet (lsquic_enc_session_t *enc_session, uint8_t path_id,
uint64_t pack_num, unsigned char *buf, size_t *header_len,
size_t data_len, unsigned char *buf_out, size_t max_out_len,
size_t *out_len)
{
int ret;
/* Comment: 12 = sizeof(dec_key_iv] 4 + sizeof(pack_num) 8 */
uint8_t nonce[12];
uint64_t path_id_packet_number;
EVP_AEAD_CTX *key = NULL;
int try_times = 0;
path_id_packet_number = combine_path_id_pack_num(path_id, pack_num);
memcpy(buf_out, buf, *header_len);
while(try_times < 2)
{
if (enc_session->have_key == 3 && try_times == 0)
{
key = enc_session->dec_ctx_f;
memcpy(nonce, enc_session->dec_key_nonce_f, 4);
LSQ_DEBUG("decrypt_packet using 'F' key...");
}
else
{
key = enc_session->dec_ctx_i;
memcpy(nonce, enc_session->dec_key_nonce_i, 4);
LSQ_DEBUG("decrypt_packet using 'I' key...");
}
memcpy(nonce + 4, &path_id_packet_number,
sizeof(path_id_packet_number));
*out_len = data_len;
ret = aes_aead_dec(key,
buf, *header_len,
nonce, 12,
buf + *header_len, data_len,
buf_out + *header_len, out_len);
if (ret != 0)
++try_times;
else
{
if (enc_session->peer_have_final_key == 0 &&
enc_session->have_key == 3 &&
try_times == 0)
{
LSQ_DEBUG("!!!decrypt_packet find peer have final key.");
enc_session->peer_have_final_key = 1;
EV_LOG_CONN_EVENT(enc_session->cid, "settled on private key "
"'%c' after %d tries (packet number %"PRIu64")",
key == enc_session->dec_ctx_f ? 'F' : 'I',
try_times, pack_num);
}
break;
}
}
// if (ret)
// {
// *out_len = data_len;
// memcpy(buf_out, buf, *header_len);
// key = "\x45\xCA\x99\x4A\x40\xBC\xE3\x3B\x32\x16\x59\x51\x98\x36\xD4\x21";
// ret = aes_aead_dec(key,
// buf, 0,
// lsquic_str_cstr(enc_session->sstk), 12,
// buf + *header_len, data_len,
// buf_out + *header_len, out_len);
// }
LSQ_DEBUG("***decrypt_packet %s.", (ret == 0 ? "succeed" : "failed"));
return ret;
}
#ifndef NDEBUG
/* 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)
{
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,
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)
{
/* 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);
enc_session->have_key = 2;
}
if (lsquic_enc_session_have_key_gt_one(enc_session))
return decrypt_packet(enc_session, path_id, pack_num, buf,
header_len, data_len, buf_out, max_out_len, out_len);
else
return verify_packet_hash(enc_session, version, buf, header_len,
data_len, buf_out, max_out_len, out_len);
}
int lsquic_enc(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,
unsigned char *buf_out, size_t max_out_len, size_t *out_len,
int is_hello)
{
uint8_t md[HS_PKT_HASH_LENGTH];
uint128 hash;
int ret;
int is_chlo = (is_hello && ((IS_SERVER(enc_session)) == 0));
int is_shlo = (is_hello && (IS_SERVER(enc_session)));
/* Comment: 12 = sizeof(dec_key_iv] 4 + sizeof(pack_num) 8 */
uint8_t nonce[12];
uint64_t path_id_packet_number;
EVP_AEAD_CTX *key;
if (enc_session)
LSQ_DEBUG("%s: hsk_state: %d", __func__, enc_session->hsk_state);
else
LSQ_DEBUG("%s: enc_session is not set", __func__);
if (!enc_session || enc_session->have_key == 0 || is_chlo)
{
*out_len = header_len + data_len + HS_PKT_HASH_LENGTH;
if (max_out_len < *out_len)
return -1;
if (version >= LSQVER_037)
{
hash = fnv1a_128_3(header, header_len, data, data_len,
(unsigned char *) "Client", 6);
}
else
{
hash = fnv1a_128_2(header, header_len, data, data_len);
}
serialize_fnv128_short(hash, md);
memcpy(buf_out, header, header_len);
memcpy(buf_out + header_len, md, HS_PKT_HASH_LENGTH);
memcpy(buf_out + header_len + HS_PKT_HASH_LENGTH, data, data_len);
return 0;
}
else
{
if (enc_session->have_key != 3 || is_shlo ||
((IS_SERVER(enc_session)) &&
enc_session->server_start_use_final_key == 0))
{
LSQ_DEBUG("lsquic_enc 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)
{
enc_session->server_start_use_final_key = 1;
}
}
else
{
LSQ_DEBUG("lsquic_enc using 'F' key...");
key = enc_session->enc_ctx_f;
memcpy(nonce, enc_session->enc_key_nonce_f, 4);
}
path_id_packet_number = combine_path_id_pack_num(path_id, pack_num);
memcpy(nonce + 4, &path_id_packet_number,
sizeof(path_id_packet_number));
memcpy(buf_out, header, header_len);
*out_len = max_out_len - header_len;
ret = aes_aead_enc(key, header, header_len, nonce, 12, data,
data_len, buf_out + header_len, out_len);
*out_len += header_len;
return ret;
}
}
int
get_peer_option (const lsquic_enc_session_t *enc_session, uint32_t tag)
{
switch (tag)
{
case QTAG_NSTP:
return !!(enc_session->hs_ctx.opts & HOPT_NSTP);
case QTAG_SREJ:
return !!(enc_session->hs_ctx.opts & HOPT_SREJ);
default:
assert(0);
return 0;
}
}
/* 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)
{
switch (tag)
{
case QTAG_TCID:
if (enc_session->hs_ctx.set & HSET_TCID)
{
*val = enc_session->hs_ctx.tcid;
return 0;
}
else
return -1;
case QTAG_SMHL:
if (enc_session->hs_ctx.set & HSET_SMHL)
{
*val = enc_session->hs_ctx.smhl;
return 0;
}
else
return -1;
}
/* XXX For the following values, there is no record which were present
* in CHLO or SHLO and which were not. Assume that zero means that
* they weren't present.
*/
switch (tag)
{
case QTAG_CFCW:
if (enc_session->hs_ctx.cfcw)
{
*val = enc_session->hs_ctx.cfcw;
return 0;
}
else
return -1;
case QTAG_SFCW:
if (enc_session->hs_ctx.sfcw)
{
*val = enc_session->hs_ctx.sfcw;
return 0;
}
else
return -1;
case QTAG_MIDS:
if (enc_session->hs_ctx.mids)
{
*val = enc_session->hs_ctx.mids;
return 0;
}
else
return -1;
default:
return -1;
}
}
#if LSQUIC_KEEP_ENC_SESS_HISTORY
void
lsquic_get_enc_hist (const lsquic_enc_session_t *enc_session,
char buf[(1 << ESHIST_BITS) + 1])
{
const unsigned hist_idx = ESHIST_MASK & enc_session->es_hist_idx;
if (enc_session->es_hist_buf[hist_idx] == ESHE_EMPTY)
memcpy(buf, enc_session->es_hist_buf, hist_idx + 1);
else
{
memcpy(buf, enc_session->es_hist_buf + hist_idx, sizeof(enc_session->es_hist_buf) - hist_idx);
memcpy(buf + hist_idx, enc_session->es_hist_buf, hist_idx);
buf[(1 << ESHIST_BITS)] = '\0';
}
}
#endif