Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional extensions and allow sending multiple tickets #328

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/picotls.h
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,10 @@ typedef struct st_ptls_handshake_properties_t {
* an optional list of additional extensions to send either in CH or EE, terminated by type == UINT16_MAX
*/
ptls_raw_extension_t *additional_extensions;
/**
* an optional list of extensions to send in NST, terminated by type == UINT16_MAX
*/
ptls_raw_extension_t *nst_extensions;
/**
* an optional callback that returns a boolean value indicating if a particular extension should be collected
*/
Expand Down
184 changes: 123 additions & 61 deletions lib/picotls.c
Original file line number Diff line number Diff line change
Expand Up @@ -1069,11 +1069,47 @@ static int derive_resumption_secret(ptls_key_schedule_t *sched, uint8_t *secret,
return ret;
}

static int should_collect_unknown_extension(ptls_t *tls, ptls_handshake_properties_t *properties, uint16_t type)
{
return properties != NULL && properties->collect_extension != NULL && properties->collect_extension(tls, properties, type);
}

static int collect_unknown_extension(ptls_t *tls, uint16_t type, const uint8_t *src, const uint8_t *const end,
ptls_raw_extension_t *slots)
{
size_t i;
for (i = 0; slots[i].type != UINT16_MAX; ++i) {
assert(i < MAX_UNKNOWN_EXTENSIONS);
if (slots[i].type == type)
return PTLS_ALERT_ILLEGAL_PARAMETER;
}
if (i < MAX_UNKNOWN_EXTENSIONS) {
slots[i].type = type;
slots[i].data = ptls_iovec_init(src, end - src);
slots[i + 1].type = UINT16_MAX;
}
return 0;
}

static int report_unknown_extensions(ptls_t *tls, ptls_handshake_properties_t *properties, ptls_raw_extension_t *slots)
{
if (properties != NULL && properties->collect_extension != NULL) {
assert(properties->collected_extensions != NULL);
return properties->collected_extensions(tls, properties, slots);
} else {
return 0;
}
}



static int decode_new_session_ticket(ptls_t *tls, uint32_t *lifetime, uint32_t *age_add, ptls_iovec_t *nonce, ptls_iovec_t *ticket,
uint32_t *max_early_data_size, const uint8_t *src, const uint8_t *const end)
uint32_t *max_early_data_size, const uint8_t *src, const uint8_t *const end, ptls_handshake_properties_t *properties)
{
uint16_t exttype;
int ret;
static const ptls_raw_extension_t no_unknown_extensions = {UINT16_MAX};
ptls_raw_extension_t *unknown_extensions = (ptls_raw_extension_t *)&no_unknown_extensions;

if ((ret = ptls_decode32(lifetime, &src, end)) != 0)
goto Exit;
Expand Down Expand Up @@ -1104,6 +1140,19 @@ static int decode_new_session_ticket(ptls_t *tls, uint32_t *lifetime, uint32_t *
goto Exit;
break;
default:
if (should_collect_unknown_extension(tls, properties, exttype)) {
if (unknown_extensions == &no_unknown_extensions) {
if ((unknown_extensions = malloc(sizeof(*unknown_extensions) * (MAX_UNKNOWN_EXTENSIONS + 1))) == NULL) {
ret = PTLS_ERROR_NO_MEMORY;
goto Exit;
}
unknown_extensions[0].type = UINT16_MAX;
}
if ((ret = collect_unknown_extension(tls, exttype, src, end, unknown_extensions)) != 0)
goto Exit;
if ((ret = report_unknown_extensions(tls, properties, unknown_extensions)) != 0)
goto Exit;
}
src = end;
break;
}
Expand All @@ -1116,7 +1165,7 @@ static int decode_new_session_ticket(ptls_t *tls, uint32_t *lifetime, uint32_t *

static int decode_stored_session_ticket(ptls_t *tls, ptls_key_exchange_algorithm_t **key_share, ptls_cipher_suite_t **cs,
ptls_iovec_t *secret, uint32_t *obfuscated_ticket_age, ptls_iovec_t *ticket,
uint32_t *max_early_data_size, const uint8_t *src, const uint8_t *const end)
uint32_t *max_early_data_size, const uint8_t *src, const uint8_t *const end, ptls_handshake_properties_t *properties)
{
uint16_t kxid, csid;
uint32_t lifetime, age_add;
Expand All @@ -1132,7 +1181,7 @@ static int decode_stored_session_ticket(ptls_t *tls, ptls_key_exchange_algorithm
if ((ret = ptls_decode16(&csid, &src, end)) != 0)
goto Exit;
ptls_decode_open_block(src, end, 3, {
if ((ret = decode_new_session_ticket(tls, &lifetime, &age_add, &nonce, ticket, max_early_data_size, src, end)) != 0)
if ((ret = decode_new_session_ticket(tls, &lifetime, &age_add, &nonce, ticket, max_early_data_size, src, end, properties)) != 0)
goto Exit;
src = end;
});
Expand Down Expand Up @@ -1403,7 +1452,23 @@ static int send_finished(ptls_t *tls, ptls_message_emitter_t *emitter)
return ret;
}

static int send_session_ticket(ptls_t *tls, ptls_message_emitter_t *emitter)
static int push_nst_extensions(ptls_handshake_properties_t *properties, ptls_buffer_t *sendbuf)
{
int ret;

if (properties != NULL && properties->nst_extensions != NULL) {
ptls_raw_extension_t *ext;
for (ext = properties->nst_extensions; ext->type != UINT16_MAX; ++ext) {
buffer_push_extension(sendbuf, ext->type, { ptls_buffer_pushv(sendbuf, ext->data.base, ext->data.len); });
}
}
ret = 0;
Exit:
return ret;
}


static int send_new_session_ticket(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_handshake_properties_t *properties)
{
ptls_hash_context_t *msghash_backup = tls->key_schedule->hashes[0].ctx->clone_(tls->key_schedule->hashes[0].ctx);
ptls_buffer_t session_id;
Expand All @@ -1414,25 +1479,6 @@ static int send_session_ticket(ptls_t *tls, ptls_message_emitter_t *emitter)
assert(tls->ctx->ticket_lifetime != 0);
assert(tls->ctx->encrypt_ticket != NULL);

{ /* calculate verify-data that will be sent by the client */
size_t orig_off = emitter->buf->off;
if (tls->pending_handshake_secret != NULL && !tls->ctx->omit_end_of_early_data) {
assert(tls->state == PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA);
ptls_buffer_push_message_body(emitter->buf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA, {});
emitter->buf->off = orig_off;
}
ptls_buffer_push_message_body(emitter->buf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_FINISHED, {
if ((ret = ptls_buffer_reserve(emitter->buf, tls->key_schedule->hashes[0].algo->digest_size)) != 0)
goto Exit;
if ((ret = calc_verify_data(emitter->buf->base + emitter->buf->off, tls->key_schedule,
tls->pending_handshake_secret != NULL ? tls->pending_handshake_secret
: tls->traffic_protection.dec.secret)) != 0)
goto Exit;
emitter->buf->off += tls->key_schedule->hashes[0].algo->digest_size;
});
emitter->buf->off = orig_off;
}

tls->ctx->random_bytes(&ticket_age_add, sizeof(ticket_age_add));

/* build the raw nsk */
Expand All @@ -1453,22 +1499,66 @@ static int send_session_ticket(ptls_t *tls, ptls_message_emitter_t *emitter)
goto Exit;
});
ptls_buffer_push_block(emitter->buf, 2, {
if (tls->ctx->max_early_data_size != 0)
if (tls->ctx->max_early_data_size != 0) {
buffer_push_extension(emitter->buf, PTLS_EXTENSION_TYPE_EARLY_DATA,
{ ptls_buffer_push32(emitter->buf, tls->ctx->max_early_data_size); });
push_nst_extensions(properties, emitter->buf);
}
});
});

Exit:
ptls_buffer_dispose(&session_id);
/* restore handshake state */
tls->key_schedule->hashes[0].ctx->final(tls->key_schedule->hashes[0].ctx, NULL, PTLS_HASH_FINAL_MODE_FREE);
tls->key_schedule->hashes[0].ctx = msghash_backup;


return ret;
}


static int send_session_ticket(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_handshake_properties_t *properties)
{
ptls_hash_context_t *msghash_backup = tls->key_schedule->hashes[0].ctx->clone_(tls->key_schedule->hashes[0].ctx);
int ret = 0;

assert(tls->ctx->ticket_lifetime != 0);
assert(tls->ctx->encrypt_ticket != NULL);

{ /* calculate verify-data that will be sent by the client */
size_t orig_off = emitter->buf->off;
if (tls->pending_handshake_secret != NULL && !tls->ctx->omit_end_of_early_data) {
assert(tls->state == PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA);
ptls_buffer_push_message_body(emitter->buf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA, {});
emitter->buf->off = orig_off;
}
ptls_buffer_push_message_body(emitter->buf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_FINISHED, {
if ((ret = ptls_buffer_reserve(emitter->buf, tls->key_schedule->hashes[0].algo->digest_size)) != 0)
goto Exit;
if ((ret = calc_verify_data(emitter->buf->base + emitter->buf->off, tls->key_schedule,
tls->pending_handshake_secret != NULL ? tls->pending_handshake_secret
: tls->traffic_protection.dec.secret)) != 0)
goto Exit;
emitter->buf->off += tls->key_schedule->hashes[0].algo->digest_size;
});
emitter->buf->off = orig_off;
}

{/* Send New Session Ticket */
if ((ret = send_new_session_ticket(tls, emitter, properties) != 0))
goto Exit;
}

Exit:
/* restore handshake state */
tls->key_schedule->hashes[0].ctx->final(tls->key_schedule->hashes[0].ctx, NULL, PTLS_HASH_FINAL_MODE_FREE);
tls->key_schedule->hashes[0].ctx = msghash_backup;

return ret;
}


static int push_change_cipher_spec(ptls_t *tls, ptls_message_emitter_t *emitter)
{
int ret;
Expand Down Expand Up @@ -1929,7 +2019,7 @@ static int send_client_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_
uint32_t max_early_data_size;
if (decode_stored_session_ticket(tls, &key_share, &cipher_suite, &resumption_secret, &obfuscated_ticket_age,
&resumption_ticket, &max_early_data_size, properties->client.session_ticket.base,
properties->client.session_ticket.base + properties->client.session_ticket.len) == 0) {
properties->client.session_ticket.base + properties->client.session_ticket.len, properties) == 0) {
tls->client.offered_psk = 1;
/* key-share selected by HRR should not be overridden */
if (tls->key_share == NULL)
Expand Down Expand Up @@ -2385,38 +2475,6 @@ static int client_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptl
return ret;
}

static int should_collect_unknown_extension(ptls_t *tls, ptls_handshake_properties_t *properties, uint16_t type)
{
return properties != NULL && properties->collect_extension != NULL && properties->collect_extension(tls, properties, type);
}

static int collect_unknown_extension(ptls_t *tls, uint16_t type, const uint8_t *src, const uint8_t *const end,
ptls_raw_extension_t *slots)
{
size_t i;
for (i = 0; slots[i].type != UINT16_MAX; ++i) {
assert(i < MAX_UNKNOWN_EXTENSIONS);
if (slots[i].type == type)
return PTLS_ALERT_ILLEGAL_PARAMETER;
}
if (i < MAX_UNKNOWN_EXTENSIONS) {
slots[i].type = type;
slots[i].data = ptls_iovec_init(src, end - src);
slots[i + 1].type = UINT16_MAX;
}
return 0;
}

static int report_unknown_extensions(ptls_t *tls, ptls_handshake_properties_t *properties, ptls_raw_extension_t *slots)
{
if (properties != NULL && properties->collect_extension != NULL) {
assert(properties->collected_extensions != NULL);
return properties->collected_extensions(tls, properties, slots);
} else {
return 0;
}
}

static int client_handle_encrypted_extensions(ptls_t *tls, ptls_iovec_t message, ptls_handshake_properties_t *properties)
{
const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len, *esni_nonce = NULL;
Expand Down Expand Up @@ -2949,7 +3007,7 @@ static int client_handle_finished(ptls_t *tls, ptls_message_emitter_t *emitter,
return ret;
}

static int client_handle_new_session_ticket(ptls_t *tls, ptls_iovec_t message)
static int client_handle_new_session_ticket(ptls_t *tls, ptls_iovec_t message, ptls_handshake_properties_t *properties)
{
const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len;
ptls_iovec_t ticket_nonce;
Expand All @@ -2959,7 +3017,7 @@ static int client_handle_new_session_ticket(ptls_t *tls, ptls_iovec_t message)
uint32_t ticket_lifetime, ticket_age_add, max_early_data_size;
ptls_iovec_t ticket;
if ((ret = decode_new_session_ticket(tls, &ticket_lifetime, &ticket_age_add, &ticket_nonce, &ticket, &max_early_data_size,
src, end)) != 0)
src, end, properties)) != 0)
return ret;
}

Expand Down Expand Up @@ -4070,7 +4128,7 @@ static int server_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptl

/* send session ticket if necessary */
if (ch->psk.ke_modes != 0 && tls->ctx->ticket_lifetime != 0) {
if ((ret = send_session_ticket(tls, emitter)) != 0)
if ((ret = send_session_ticket(tls, emitter, properties)) != 0)
goto Exit;
}

Expand Down Expand Up @@ -4478,7 +4536,7 @@ static int handle_client_handshake_message(ptls_t *tls, ptls_message_emitter_t *
case PTLS_STATE_CLIENT_POST_HANDSHAKE:
switch (type) {
case PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET:
ret = client_handle_new_session_ticket(tls, message);
ret = client_handle_new_session_ticket(tls, message, properties);
break;
case PTLS_HANDSHAKE_TYPE_KEY_UPDATE:
ret = handle_key_update(tls, emitter, message);
Expand Down Expand Up @@ -5335,6 +5393,10 @@ int ptls_server_handle_message(ptls_t *tls, ptls_buffer_t *sendbuf, size_t epoch
{sendbuf, &tls->traffic_protection.enc, 0, begin_raw_message, commit_raw_message}, SIZE_MAX, epoch_offsets};
struct st_ptls_record_t rec = {PTLS_CONTENT_TYPE_HANDSHAKE, 0, inlen, input};

/* If necessary, can be used to send multiple session tickets during the same connection */
if (input == NULL)
return send_new_session_ticket(tls, &emitter.super, properties);

assert(input);

if (ptls_get_read_epoch(tls) != in_epoch)
Expand Down