From 4b3644a30dfc8e9546501ef0dfde6059f07e814a Mon Sep 17 00:00:00 2001 From: SIMO Francklin Date: Sun, 25 Oct 2020 16:39:34 +0000 Subject: [PATCH 1/2] add additional extensions to send in NST message --- include/picotls.h | 4 ++ lib/picotls.c | 118 +++++++++++++++++++++++++++++----------------- 2 files changed, 80 insertions(+), 42 deletions(-) diff --git a/include/picotls.h b/include/picotls.h index 0ffe7d719..327c761a4 100644 --- a/include/picotls.h +++ b/include/picotls.h @@ -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 */ diff --git a/lib/picotls.c b/lib/picotls.c index 9f841207e..194b2e317 100644 --- a/lib/picotls.c +++ b/lib/picotls.c @@ -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; @@ -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; } @@ -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; @@ -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; }); @@ -1403,7 +1452,22 @@ 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_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; @@ -1453,9 +1517,11 @@ 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); + } }); }); @@ -1929,7 +1995,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) @@ -2385,38 +2451,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; @@ -2949,7 +2983,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; @@ -2959,7 +2993,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; } @@ -4070,7 +4104,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; } @@ -4478,7 +4512,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); From 61dc45c8a5dd872f0a28e4fbaf8e4f1773fb2c92 Mon Sep 17 00:00:00 2001 From: SIMO Francklin Date: Mon, 26 Oct 2020 10:00:15 +0000 Subject: [PATCH 2/2] server can send multiple session tickets --- lib/picotls.c | 68 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/lib/picotls.c b/lib/picotls.c index 194b2e317..8c680cb7d 100644 --- a/lib/picotls.c +++ b/lib/picotls.c @@ -1467,7 +1467,8 @@ static int push_nst_extensions(ptls_handshake_properties_t *properties, ptls_buf return ret; } -static int send_session_ticket(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_handshake_properties_t *properties) + +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; @@ -1478,25 +1479,6 @@ static int send_session_ticket(ptls_t *tls, ptls_message_emitter_t *emitter, ptl 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 */ @@ -1527,7 +1509,48 @@ static int send_session_ticket(ptls_t *tls, ptls_message_emitter_t *emitter, ptl 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; @@ -1535,6 +1558,7 @@ static int send_session_ticket(ptls_t *tls, ptls_message_emitter_t *emitter, ptl return ret; } + static int push_change_cipher_spec(ptls_t *tls, ptls_message_emitter_t *emitter) { int ret; @@ -5369,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)