From c3e903d49e07d2e80d59481fba84063a4a8356e6 Mon Sep 17 00:00:00 2001 From: Frederik Deweerdt Date: Tue, 3 Oct 2017 09:32:55 -0700 Subject: [PATCH 1/8] Add a cast to be able to include the header in a C++ file --- include/picotls.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/picotls.h b/include/picotls.h index e6864ac10..00a3a28b1 100644 --- a/include/picotls.h +++ b/include/picotls.h @@ -766,7 +766,7 @@ inline ptls_iovec_t ptls_iovec_init(const void *p, size_t len) inline void ptls_buffer_init(ptls_buffer_t *buf, void *smallbuf, size_t smallbuf_size) { assert(smallbuf != NULL); - buf->base = smallbuf; + buf->base = (uint8_t *)smallbuf; buf->off = 0; buf->capacity = smallbuf_size; buf->is_allocated = 0; From 97beb4308111764902f1d9062ca04c55006a72fd Mon Sep 17 00:00:00 2001 From: Frederik Deweerdt Date: Tue, 3 Oct 2017 11:24:50 -0700 Subject: [PATCH 2/8] Add an `extern "C"` directive --- include/picotls.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/picotls.h b/include/picotls.h index 00a3a28b1..921a72324 100644 --- a/include/picotls.h +++ b/include/picotls.h @@ -22,6 +22,10 @@ #ifndef picotls_h #define picotls_h +#ifdef __cplusplus +extern "C" { +#endif + #include #include #include @@ -807,4 +811,8 @@ inline size_t ptls_aead_decrypt(ptls_aead_context_t *ctx, void *output, const vo int ptls_load_certificates(ptls_context_t *ctx, char *cert_pem_file); +#ifdef __cplusplus +} +#endif + #endif From 6e3815a0bb5ddcfe4abdca74f5a216a1d00f0a66 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Wed, 4 Oct 2017 01:07:12 +0100 Subject: [PATCH 3/8] cli: Fix IPv6 support for server role --- t/cli.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/cli.c b/t/cli.c index 06a586873..744c61f8b 100644 --- a/t/cli.c +++ b/t/cli.c @@ -226,7 +226,7 @@ static int run_server(struct sockaddr *sa, socklen_t salen, ptls_context_t *ctx, { int listen_fd, conn_fd, on = 1; - if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + if ((listen_fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) { perror("socket(2) failed"); return 1; } From a576a89add5a399519b58034eac77c159eb820f7 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Wed, 4 Oct 2017 01:13:46 +0100 Subject: [PATCH 4/8] cli: add -4 and -6 options to force IP version localhost may resolve to 127.0.0.1 and ::1, but a peer could be limited to one of them. --- t/cli.c | 13 +++++++++++-- t/util.h | 3 ++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/t/cli.c b/t/cli.c index 744c61f8b..775fd8efc 100644 --- a/t/cli.c +++ b/t/cli.c @@ -275,6 +275,8 @@ static void usage(const char *cmd) printf("Usage: %s [options] host port\n" "\n" "Options:\n" + " -4 force IPv4\n" + " -6 force IPv6\n" " -c certificate-file\n" " -k key-file specifies the credentials to be used for running the\n" " server. If omitted, the command runs as a client.\n" @@ -305,9 +307,16 @@ int main(int argc, char **argv) int use_early_data = 0, ch; struct sockaddr_storage sa; socklen_t salen; + int family = 0; - while ((ch = getopt(argc, argv, "c:k:es:l:vh")) != -1) { + while ((ch = getopt(argc, argv, "46c:k:es:l:vh")) != -1) { switch (ch) { + case '4': + family = AF_INET; + break; + case '6': + family = AF_INET6; + break; case 'c': load_certificate_chain(&ctx, optarg); break; @@ -354,7 +363,7 @@ int main(int argc, char **argv) host = (--argc, *argv++); port = (--argc, *argv++); - if (resolve_address((struct sockaddr *)&sa, &salen, host, port, SOCK_STREAM, IPPROTO_TCP) != 0) + if (resolve_address((struct sockaddr *)&sa, &salen, host, port, family, SOCK_STREAM, IPPROTO_TCP) != 0) exit(1); if (ctx.certificates.count != 0) { diff --git a/t/util.h b/t/util.h index eff0e4f8b..47010fa5d 100644 --- a/t/util.h +++ b/t/util.h @@ -231,12 +231,13 @@ static inline void setup_session_cache(ptls_context_t *ctx) ctx->encrypt_ticket = &sc.super; } -static inline int resolve_address(struct sockaddr *sa, socklen_t *salen, const char *host, const char *port, int type, int proto) +static inline int resolve_address(struct sockaddr *sa, socklen_t *salen, const char *host, const char *port, int family, int type, int proto) { struct addrinfo hints, *res; int err; memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; hints.ai_socktype = type; hints.ai_protocol = proto; hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV | AI_PASSIVE; From 46554f0f4e09531015bd9939d34c7b6a90305524 Mon Sep 17 00:00:00 2001 From: Kazuho Oku Date: Tue, 3 Oct 2017 23:58:18 -0700 Subject: [PATCH 5/8] send HRR.KeyShare only if the client needs to update CH.KeyShare MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit draft-21 4.2.8: > Upon receipt of this extension in a HelloRetryRequest, the client MUST verify that (1) the selected_group field corresponds to a group which was provided in the “supported_groups” extension in the original ClientHello; and (2) the selected_group field does not correspond to a group which was provided in the “key_share” extension in the original ClientHello. --- lib/picotls.c | 49 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/lib/picotls.c b/lib/picotls.c index c6eb81517..deae3154b 100644 --- a/lib/picotls.c +++ b/lib/picotls.c @@ -231,6 +231,7 @@ struct st_ptls_client_hello_t { ptls_iovec_t tbs; ptls_iovec_t ch1_hash; ptls_iovec_t signature; + unsigned sent_key_share : 1; } cookie; struct { const uint8_t *hash_end; @@ -1974,10 +1975,25 @@ static int decode_client_hello(ptls_t *tls, struct st_ptls_client_hello_t *ch, c ptls_decode_block(src, end, 2, { ch->cookie.tbs.base = (void *)src; ptls_decode_open_block(src, end, 2, { - ptls_decode_block(src, end, 1, { + ptls_decode_open_block(src, end, 1, { ch->cookie.ch1_hash = ptls_iovec_init(src, end - src); src = end; }); + if (src == end) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + switch (*src++) { + case 0: + assert(!ch->cookie.sent_key_share); + break; + case 1: + ch->cookie.sent_key_share = 1; + break; + default: + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } }); ch->cookie.tbs.len = src - ch->cookie.tbs.base; ptls_decode_block(src, end, 1, { @@ -2219,17 +2235,22 @@ static int calc_cookie_signature(ptls_t *tls, ptls_handshake_properties_t *prope static int server_handle_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t message, ptls_handshake_properties_t *properties) { #define EMIT_HRR(sched, negotiated_group, additional_extensions) \ - buffer_push_handshake(sendbuf, sched, PTLS_HANDSHAKE_TYPE_HELLO_RETRY_REQUEST, { \ - ptls_buffer_push16(sendbuf, PTLS_PROTOCOL_VERSION_DRAFT21); \ - ptls_buffer_push16(sendbuf, tls->cipher_suite->id); \ - ptls_buffer_push_block(sendbuf, 2, { \ - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE, \ - { ptls_buffer_push16(sendbuf, (negotiated_group)->id); }); \ - { \ - additional_extensions \ - } \ + do { \ + ptls_key_exchange_algorithm_t *_negotiated_group = (negotiated_group); \ + buffer_push_handshake(sendbuf, (sched), PTLS_HANDSHAKE_TYPE_HELLO_RETRY_REQUEST, { \ + ptls_buffer_push16(sendbuf, PTLS_PROTOCOL_VERSION_DRAFT21); \ + ptls_buffer_push16(sendbuf, tls->cipher_suite->id); \ + ptls_buffer_push_block(sendbuf, 2, { \ + if (_negotiated_group != NULL) { \ + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE, \ + { ptls_buffer_push16(sendbuf, _negotiated_group->id); }); \ + } \ + { \ + additional_extensions \ + } \ + }); \ }); \ - }) + } while (0) struct st_ptls_client_hello_t ch = {NULL, {NULL}, 0, {NULL}, {NULL}, {NULL}, {{0}}, {NULL}, {{{NULL}}}, {{NULL}}, {NULL}, {{UINT16_MAX}}}; @@ -2318,7 +2339,7 @@ static int server_handle_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0)); /* ... reusing sendbuf to rebuild HRR for hash calculation */ size_t hrr_start = sendbuf->off; - EMIT_HRR(tls->key_schedule, key_share.algorithm, { + EMIT_HRR(tls->key_schedule, ch.cookie.sent_key_share ? key_share.algorithm : NULL, { buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_COOKIE, { ptls_buffer_pushv(sendbuf, ch.cookie.all.base, ch.cookie.all.len); }); }); @@ -2340,7 +2361,7 @@ static int server_handle_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0)); } /* emit HelloRetryRequest */ - EMIT_HRR(enforce_cookie_use ? NULL : tls->key_schedule, negotiated_group, { + EMIT_HRR(enforce_cookie_use ? NULL : tls->key_schedule, key_share.algorithm != NULL ? NULL : negotiated_group, { if (enforce_cookie_use) { buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_COOKIE, { ptls_buffer_push_block(sendbuf, 2, { @@ -2355,6 +2376,8 @@ static int server_handle_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t key_schedule_extract_ch1hash(tls->key_schedule, sendbuf->base + sendbuf->off); sendbuf->off += sz; }); + /* second is if we have sent key_share extension */ + ptls_buffer_push(sendbuf, key_share.algorithm == NULL); /* we can add more data here */ }); size_t tbs_len = sendbuf->off - tbs_start; From b90c26401a34dd223091be83685d3066e9b2a5f6 Mon Sep 17 00:00:00 2001 From: Kazuho Oku Date: Wed, 4 Oct 2017 12:12:27 -0700 Subject: [PATCH 6/8] split large handshake message into multiple records; also do in-place encryption for small messages --- lib/picotls.c | 253 ++++++++++++++++++++++++++++---------------------- 1 file changed, 141 insertions(+), 112 deletions(-) diff --git a/lib/picotls.c b/lib/picotls.c index deae3154b..fdada16f7 100644 --- a/lib/picotls.c +++ b/lib/picotls.c @@ -469,21 +469,65 @@ static int aead_decrypt(struct st_ptls_traffic_protection_t *ctx, void *output, return 0; } +static int buffer_push_encrypted_record(ptls_buffer_t *buf, uint8_t type, const void *src, size_t len, + struct st_ptls_traffic_protection_t *enc) +{ + int ret; + + if ((ret = ptls_buffer_reserve(buf, 5 + len + 1 + enc->aead->algo->tag_size)) != 0) + return ret; + + len = aead_encrypt(enc, buf->base + buf->off + 5, src, len, type); + buf->base[buf->off] = PTLS_CONTENT_TYPE_APPDATA; + buf->base[buf->off + 1] = 3; + buf->base[buf->off + 2] = 1; + buf->base[buf->off + 3] = (len >> 8) & 0xff; + buf->base[buf->off + 4] = len & 0xff; + buf->off += 5 + len; + return 0; +} + static int buffer_encrypt_record(ptls_buffer_t *buf, size_t rec_start, struct st_ptls_traffic_protection_t *enc) { - uint8_t encrypted[PTLS_MAX_ENCRYPTED_RECORD_SIZE]; - size_t enclen, bodylen = buf->off - rec_start - 5; + size_t bodylen = buf->off - rec_start - 5, off; + uint8_t *tmpbuf, type = buf->base[rec_start]; int ret; - assert(bodylen <= PTLS_MAX_PLAINTEXT_RECORD_SIZE); + /* do in-place encryption if only one record needs to be emitted */ + if (bodylen <= PTLS_MAX_PLAINTEXT_RECORD_SIZE) { + buf->off = rec_start; + return buffer_push_encrypted_record(buf, type, buf->base + rec_start + 5, bodylen, enc); + } + + /* copy plaintext to temporary buffer */ + if ((tmpbuf = malloc(bodylen)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + memcpy(tmpbuf, buf->base + rec_start + 5, bodylen); - enclen = aead_encrypt(enc, encrypted, buf->base + rec_start + 5, bodylen, buf->base[rec_start]); + /* prepare buffer */ buf->off = rec_start; - ptls_buffer_push(buf, PTLS_CONTENT_TYPE_APPDATA, 3, 1); - ptls_buffer_push16(buf, (uint16_t)enclen); - ptls_buffer_pushv(buf, encrypted, enclen); + if ((ret = ptls_buffer_reserve( + buf, bodylen + (bodylen + PTLS_MAX_PLAINTEXT_RECORD_SIZE - 1) / PTLS_MAX_PLAINTEXT_RECORD_SIZE * 5)) != 0) + goto Exit; + + /* emit encrypted records */ + off = 0; + do { + size_t rec_size = bodylen - off; + if (rec_size > PTLS_MAX_PLAINTEXT_RECORD_SIZE) + rec_size = PTLS_MAX_PLAINTEXT_RECORD_SIZE; + if ((ret = buffer_push_encrypted_record(buf, type, tmpbuf + off, rec_size, enc)) != 0) + goto Exit; + off += rec_size; + } while (off < bodylen); Exit: + if (tmpbuf != NULL) { + ptls_clear_memory(tmpbuf, bodylen); + free(tmpbuf); + } return ret; } @@ -493,16 +537,6 @@ static int buffer_encrypt_record(ptls_buffer_t *buf, size_t rec_start, struct st ptls_buffer_push_block((buf), 2, block); \ } while (0) -#define buffer_encrypt(buf, enc, block) \ - do { \ - size_t rec_start = (buf)->off; \ - do { \ - block \ - } while (0); \ - if ((ret = buffer_encrypt_record((buf), rec_start, (enc))) != 0) \ - goto Exit; \ - } while (0); - #define buffer_push_handshake_core(buf, key_sched, type, mess_start, block) \ do { \ ptls_buffer_push((buf), (type)); \ @@ -512,14 +546,19 @@ static int buffer_encrypt_record(ptls_buffer_t *buf, size_t rec_start, struct st } while (0); \ }); \ if ((key_sched) != NULL) \ - key_schedule_update_hash((key_sched), (buf)->base + mess_start, (buf)->off - (mess_start)); \ + key_schedule_update_hash((key_sched), (buf)->base + (mess_start), (buf)->off - (mess_start)); \ } while (0) -#define buffer_push_handshake(buf, key_sched, type, block) \ - buffer_push_record((buf), PTLS_CONTENT_TYPE_HANDSHAKE, { \ - size_t mess_start = (buf)->off; \ - buffer_push_handshake_core((buf), (key_sched), (type), mess_start, block); \ - }) +#define buffer_push_handshake(buf, key_sched, enc, type, block) \ + do { \ + size_t rec_start = (buf)->off; \ + buffer_push_record((buf), PTLS_CONTENT_TYPE_HANDSHAKE, \ + { buffer_push_handshake_core((buf), (key_sched), (type), rec_start + 5, block); }); \ + if ((enc) != NULL) { \ + if ((ret = buffer_encrypt_record((buf), rec_start, (enc))) != 0) \ + goto Exit; \ + } \ + } while (0) #define buffer_calc_handshake_hash(buf, key_sched, type, block) \ do { \ @@ -1005,14 +1044,12 @@ static int send_finished(ptls_t *tls, ptls_buffer_t *sendbuf) { int ret; - buffer_encrypt(sendbuf, &tls->traffic_protection.enc, { - buffer_push_handshake(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_FINISHED, { - if ((ret = ptls_buffer_reserve(sendbuf, tls->key_schedule->algo->digest_size)) != 0) - goto Exit; - if ((ret = calc_verify_data(sendbuf->base + sendbuf->off, tls->key_schedule, tls->traffic_protection.enc.secret)) != 0) - goto Exit; - sendbuf->off += tls->key_schedule->algo->digest_size; - }); + buffer_push_handshake(sendbuf, tls->key_schedule, &tls->traffic_protection.enc, PTLS_HANDSHAKE_TYPE_FINISHED, { + if ((ret = ptls_buffer_reserve(sendbuf, tls->key_schedule->algo->digest_size)) != 0) + goto Exit; + if ((ret = calc_verify_data(sendbuf->base + sendbuf->off, tls->key_schedule, tls->traffic_protection.enc.secret)) != 0) + goto Exit; + sendbuf->off += tls->key_schedule->algo->digest_size; }); Exit: @@ -1051,21 +1088,19 @@ static int send_session_ticket(ptls_t *tls, ptls_buffer_t *sendbuf) goto Exit; /* encrypt and send */ - buffer_encrypt(sendbuf, &tls->traffic_protection.enc, { - buffer_push_handshake(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET, { - ptls_buffer_push32(sendbuf, tls->ctx->ticket_lifetime); - ptls_buffer_push32(sendbuf, ticket_age_add); - ptls_buffer_push_block(sendbuf, 1, {}); - ptls_buffer_push_block(sendbuf, 2, { - if ((ret = tls->ctx->encrypt_ticket->cb(tls->ctx->encrypt_ticket, tls, 1, sendbuf, - ptls_iovec_init(session_id.base, session_id.off))) != 0) - goto Exit; - }); - ptls_buffer_push_block(sendbuf, 2, { - if (tls->ctx->max_early_data_size != 0) - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_EARLY_DATA, - { ptls_buffer_push32(sendbuf, tls->ctx->max_early_data_size); }); - }); + buffer_push_handshake(sendbuf, tls->key_schedule, &tls->traffic_protection.enc, PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET, { + ptls_buffer_push32(sendbuf, tls->ctx->ticket_lifetime); + ptls_buffer_push32(sendbuf, ticket_age_add); + ptls_buffer_push_block(sendbuf, 1, {}); + ptls_buffer_push_block(sendbuf, 2, { + if ((ret = tls->ctx->encrypt_ticket->cb(tls->ctx->encrypt_ticket, tls, 1, sendbuf, + ptls_iovec_init(session_id.base, session_id.off))) != 0) + goto Exit; + }); + ptls_buffer_push_block(sendbuf, 2, { + if (tls->ctx->max_early_data_size != 0) + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_EARLY_DATA, + { ptls_buffer_push32(sendbuf, tls->ctx->max_early_data_size); }); }); }); @@ -1139,7 +1174,7 @@ static int send_client_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_handshake } msghash_off = sendbuf->off + 5; - buffer_push_handshake(sendbuf, NULL, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, { + buffer_push_handshake(sendbuf, NULL, NULL, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, { /* legacy_version */ ptls_buffer_push16(sendbuf, 0x0303); /* random_bytes */ @@ -1694,8 +1729,8 @@ static int client_handle_finished(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iove if (tls->early_data != NULL) { assert(tls->traffic_protection.enc.aead != NULL); if (!tls->skip_early_data) { - buffer_encrypt(sendbuf, &tls->traffic_protection.enc, - { buffer_push_handshake(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA, {}); }); + buffer_push_handshake(sendbuf, tls->key_schedule, &tls->traffic_protection.enc, PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA, + {}); } if ((ret = retire_early_data_secret(tls, 1)) != 0) goto Exit; @@ -2237,7 +2272,7 @@ static int server_handle_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t #define EMIT_HRR(sched, negotiated_group, additional_extensions) \ do { \ ptls_key_exchange_algorithm_t *_negotiated_group = (negotiated_group); \ - buffer_push_handshake(sendbuf, (sched), PTLS_HANDSHAKE_TYPE_HELLO_RETRY_REQUEST, { \ + buffer_push_handshake(sendbuf, (sched), NULL, PTLS_HANDSHAKE_TYPE_HELLO_RETRY_REQUEST, { \ ptls_buffer_push16(sendbuf, PTLS_PROTOCOL_VERSION_DRAFT21); \ ptls_buffer_push16(sendbuf, tls->cipher_suite->id); \ ptls_buffer_push_block(sendbuf, 2, { \ @@ -2472,7 +2507,7 @@ static int server_handle_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t } /* send ServerHello */ - buffer_push_handshake(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_SERVER_HELLO, { + buffer_push_handshake(sendbuf, tls->key_schedule, NULL, PTLS_HANDSHAKE_TYPE_SERVER_HELLO, { ptls_buffer_push16(sendbuf, PTLS_PROTOCOL_VERSION_DRAFT21); if ((ret = ptls_buffer_reserve(sendbuf, PTLS_HELLO_RANDOM_SIZE)) != 0) goto Exit; @@ -2509,78 +2544,72 @@ static int server_handle_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t } /* send EncryptedExtensions */ - buffer_encrypt(sendbuf, &tls->traffic_protection.enc, { - buffer_push_handshake(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, { - ptls_buffer_push_block(sendbuf, 2, { - if (tls->server_name != NULL) { - /* In this event, the server SHALL include an extension of type "server_name" in the (extended) server - * hello. The "extension_data" field of this extension SHALL be empty. (RFC 6066 section 3) */ - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_NAME, {}); - } - if (tls->negotiated_protocol != NULL) { - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ALPN, { - ptls_buffer_push_block(sendbuf, 2, { - ptls_buffer_push_block(sendbuf, 1, { - ptls_buffer_pushv(sendbuf, tls->negotiated_protocol, strlen(tls->negotiated_protocol)); - }); + buffer_push_handshake(sendbuf, tls->key_schedule, &tls->traffic_protection.enc, PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, { + ptls_buffer_push_block(sendbuf, 2, { + if (tls->server_name != NULL) { + /* In this event, the server SHALL include an extension of type "server_name" in the (extended) server + * hello. The "extension_data" field of this extension SHALL be empty. (RFC 6066 section 3) */ + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_NAME, {}); + } + if (tls->negotiated_protocol != NULL) { + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ALPN, { + ptls_buffer_push_block(sendbuf, 2, { + ptls_buffer_push_block(sendbuf, 1, { + ptls_buffer_pushv(sendbuf, tls->negotiated_protocol, strlen(tls->negotiated_protocol)); }); }); - } - if (tls->early_data != NULL && tls->traffic_protection.dec.aead != NULL) - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_EARLY_DATA, {}); - if ((ret = push_additional_extensions(properties, sendbuf)) != 0) - goto Exit; - }); + }); + } + if (tls->early_data != NULL && tls->traffic_protection.dec.aead != NULL) + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_EARLY_DATA, {}); + if ((ret = push_additional_extensions(properties, sendbuf)) != 0) + goto Exit; }); }); if (mode == HANDSHAKE_MODE_FULL) { /* send Certificate */ - buffer_encrypt(sendbuf, &tls->traffic_protection.enc, { - buffer_push_handshake(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_CERTIFICATE, { - ptls_buffer_push(sendbuf, 0); - ptls_buffer_push_block(sendbuf, 3, { - size_t i; - for (i = 0; i != tls->ctx->certificates.count; ++i) { - ptls_buffer_push_block(sendbuf, 3, { - ptls_buffer_pushv(sendbuf, tls->ctx->certificates.list[i].base, tls->ctx->certificates.list[i].len); - }); - ptls_buffer_push_block(sendbuf, 2, { - /* emit OCSP stapling only when requested and when the callback successfully returns one */ - if (ch.status_request && i == 0 && tls->ctx->staple_ocsp != NULL) { - size_t reset_off_to = sendbuf->off; - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_STATUS_REQUEST, { - ptls_buffer_push(sendbuf, 1); /* status_type == ocsp */ - ptls_buffer_push_block(sendbuf, 3, { - if ((ret = tls->ctx->staple_ocsp->cb(tls->ctx->staple_ocsp, tls, sendbuf, i)) == 0) - reset_off_to = 0; - }); + buffer_push_handshake(sendbuf, tls->key_schedule, &tls->traffic_protection.enc, PTLS_HANDSHAKE_TYPE_CERTIFICATE, { + ptls_buffer_push(sendbuf, 0); + ptls_buffer_push_block(sendbuf, 3, { + size_t i; + for (i = 0; i != tls->ctx->certificates.count; ++i) { + ptls_buffer_push_block(sendbuf, 3, { + ptls_buffer_pushv(sendbuf, tls->ctx->certificates.list[i].base, tls->ctx->certificates.list[i].len); + }); + ptls_buffer_push_block(sendbuf, 2, { + /* emit OCSP stapling only when requested and when the callback successfully returns one */ + if (ch.status_request && i == 0 && tls->ctx->staple_ocsp != NULL) { + size_t reset_off_to = sendbuf->off; + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_STATUS_REQUEST, { + ptls_buffer_push(sendbuf, 1); /* status_type == ocsp */ + ptls_buffer_push_block(sendbuf, 3, { + if ((ret = tls->ctx->staple_ocsp->cb(tls->ctx->staple_ocsp, tls, sendbuf, i)) == 0) + reset_off_to = 0; }); - if (reset_off_to != 0) - sendbuf->off = reset_off_to; - } - }); - } - }); + }); + if (reset_off_to != 0) + sendbuf->off = reset_off_to; + } + }); + } }); }); /* build and send CertificateVerify */ - buffer_encrypt(sendbuf, &tls->traffic_protection.enc, { - buffer_push_handshake(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY, { - size_t algo_off = sendbuf->off; - ptls_buffer_push16(sendbuf, 0); /* filled in later */ - ptls_buffer_push_block(sendbuf, 2, { - uint16_t algo; - uint8_t data[PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE]; - size_t datalen = - build_certificate_verify_signdata(data, tls->key_schedule, PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING); - if ((ret = tls->ctx->sign_certificate->cb(tls->ctx->sign_certificate, tls, &algo, sendbuf, - ptls_iovec_init(data, datalen), ch.signature_algorithms.list, - ch.signature_algorithms.count)) != 0) - goto Exit; - sendbuf->base[algo_off] = (uint8_t)(algo >> 8); - sendbuf->base[algo_off + 1] = (uint8_t)algo; - }); + buffer_push_handshake(sendbuf, tls->key_schedule, &tls->traffic_protection.enc, PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY, { + size_t algo_off = sendbuf->off; + ptls_buffer_push16(sendbuf, 0); /* filled in later */ + ptls_buffer_push_block(sendbuf, 2, { + uint16_t algo; + uint8_t data[PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE]; + size_t datalen = + build_certificate_verify_signdata(data, tls->key_schedule, PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING); + if ((ret = tls->ctx->sign_certificate->cb(tls->ctx->sign_certificate, tls, &algo, sendbuf, + ptls_iovec_init(data, datalen), ch.signature_algorithms.list, + ch.signature_algorithms.count)) != 0) + goto Exit; + sendbuf->base[algo_off] = (uint8_t)(algo >> 8); + sendbuf->base[algo_off + 1] = (uint8_t)algo; }); }); } From ef7781c9429dcf473ae8459888d2b3e0d202eec6 Mon Sep 17 00:00:00 2001 From: Kazuho Oku Date: Wed, 4 Oct 2017 14:26:12 -0700 Subject: [PATCH 7/8] handle_handshake_record should return IN_PROGRESS in case there's a partial message in queue --- lib/picotls.c | 1 + t/picotls.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/picotls.c b/lib/picotls.c index fdada16f7..d0201722f 100644 --- a/lib/picotls.c +++ b/lib/picotls.c @@ -3043,6 +3043,7 @@ static int handle_handshake_record(ptls_t *tls, int (*cb)(ptls_t *tls, ptls_buff memmove(tls->recvbuf.mess.base, src, src_end - src); } tls->recvbuf.mess.off = src_end - src; + ret = PTLS_ERROR_IN_PROGRESS; } else { ptls_buffer_dispose(&tls->recvbuf.mess); } diff --git a/t/picotls.c b/t/picotls.c index 1e49408aa..31bb6d1ed 100644 --- a/t/picotls.c +++ b/t/picotls.c @@ -221,14 +221,14 @@ static void test_fragmented_message(void) SET_RECORD("\x01\x00\x00\x03" "a"); ret = handle_handshake_record(&tls, test_fragmented_message_record, NULL, &rec, NULL); - ok(ret == 0); + ok(ret == PTLS_ERROR_IN_PROGRESS); ok(tls.recvbuf.mess.base != NULL); ok(test_fragmented_message_queue.count == 0); SET_RECORD("bc\x02\x00\x00\x02" "de" "\x03"); ret = handle_handshake_record(&tls, test_fragmented_message_record, NULL, &rec, NULL); - ok(ret == 0); + ok(ret == PTLS_ERROR_IN_PROGRESS); ok(test_fragmented_message_queue.count == 2); ok(test_fragmented_message_queue.vec[0].len == 7); ok(memcmp(test_fragmented_message_queue.vec[0].buf, "\x01\x00\x00\x03" From be3fa3dfca2a1d43cd16cb3d802a1413e1a61941 Mon Sep 17 00:00:00 2001 From: Kazuho Oku Date: Thu, 5 Oct 2017 04:07:08 -0700 Subject: [PATCH 8/8] fixes bug relying on ptls_buffer_reserve _not_ modifying the contents after `off`, and refactors --- lib/picotls.c | 100 +++++++++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 59 deletions(-) diff --git a/lib/picotls.c b/lib/picotls.c index d0201722f..1facda18c 100644 --- a/lib/picotls.c +++ b/lib/picotls.c @@ -469,59 +469,65 @@ static int aead_decrypt(struct st_ptls_traffic_protection_t *ctx, void *output, return 0; } -static int buffer_push_encrypted_record(ptls_buffer_t *buf, uint8_t type, const void *src, size_t len, - struct st_ptls_traffic_protection_t *enc) +#define buffer_push_record(buf, type, block) \ + do { \ + ptls_buffer_push((buf), (type), PTLS_RECORD_VERSION_MAJOR, PTLS_RECORD_VERSION_MINOR); \ + ptls_buffer_push_block((buf), 2, block); \ + } while (0) + +static int buffer_push_encrypted_records(ptls_buffer_t *buf, uint8_t type, const uint8_t *src, size_t len, + struct st_ptls_traffic_protection_t *enc) { - int ret; + int ret = 0; - if ((ret = ptls_buffer_reserve(buf, 5 + len + 1 + enc->aead->algo->tag_size)) != 0) - return ret; + while (len != 0) { + size_t chunk_size = len; + if (chunk_size > PTLS_MAX_PLAINTEXT_RECORD_SIZE) + chunk_size = PTLS_MAX_PLAINTEXT_RECORD_SIZE; + buffer_push_record(buf, PTLS_CONTENT_TYPE_APPDATA, { + if ((ret = ptls_buffer_reserve(buf, chunk_size + enc->aead->algo->tag_size + 1)) != 0) + goto Exit; + buf->off += aead_encrypt(enc, buf->base + buf->off, src, chunk_size, type); + }); + src += chunk_size; + len -= chunk_size; + } - len = aead_encrypt(enc, buf->base + buf->off + 5, src, len, type); - buf->base[buf->off] = PTLS_CONTENT_TYPE_APPDATA; - buf->base[buf->off + 1] = 3; - buf->base[buf->off + 2] = 1; - buf->base[buf->off + 3] = (len >> 8) & 0xff; - buf->base[buf->off + 4] = len & 0xff; - buf->off += 5 + len; - return 0; +Exit: + return ret; } static int buffer_encrypt_record(ptls_buffer_t *buf, size_t rec_start, struct st_ptls_traffic_protection_t *enc) { - size_t bodylen = buf->off - rec_start - 5, off; + size_t bodylen = buf->off - rec_start - 5; uint8_t *tmpbuf, type = buf->base[rec_start]; int ret; - /* do in-place encryption if only one record needs to be emitted */ + /* fast path: do in-place encryption if only one record needs to be emitted */ if (bodylen <= PTLS_MAX_PLAINTEXT_RECORD_SIZE) { - buf->off = rec_start; - return buffer_push_encrypted_record(buf, type, buf->base + rec_start + 5, bodylen, enc); + size_t overhead = 1 + enc->aead->algo->tag_size; + if ((ret = ptls_buffer_reserve(buf, overhead)) != 0) + return ret; + size_t encrypted_len = aead_encrypt(enc, buf->base + rec_start + 5, buf->base + rec_start + 5, bodylen, type); + assert(encrypted_len == bodylen + overhead); + buf->off += overhead; + buf->base[rec_start] = PTLS_CONTENT_TYPE_APPDATA; + buf->base[rec_start + 3] = (encrypted_len >> 8) & 0xff; + buf->base[rec_start + 4] = encrypted_len & 0xff; + return 0; } - /* copy plaintext to temporary buffer */ + /* move plaintext to temporary buffer */ if ((tmpbuf = malloc(bodylen)) == NULL) { ret = PTLS_ERROR_NO_MEMORY; goto Exit; } memcpy(tmpbuf, buf->base + rec_start + 5, bodylen); - - /* prepare buffer */ + ptls_clear_memory(buf->base + rec_start, bodylen + 5); buf->off = rec_start; - if ((ret = ptls_buffer_reserve( - buf, bodylen + (bodylen + PTLS_MAX_PLAINTEXT_RECORD_SIZE - 1) / PTLS_MAX_PLAINTEXT_RECORD_SIZE * 5)) != 0) - goto Exit; - /* emit encrypted records */ - off = 0; - do { - size_t rec_size = bodylen - off; - if (rec_size > PTLS_MAX_PLAINTEXT_RECORD_SIZE) - rec_size = PTLS_MAX_PLAINTEXT_RECORD_SIZE; - if ((ret = buffer_push_encrypted_record(buf, type, tmpbuf + off, rec_size, enc)) != 0) - goto Exit; - off += rec_size; - } while (off < bodylen); + /* push encrypted records */ + ret = buffer_push_encrypted_records(buf, type, tmpbuf, bodylen, enc); Exit: if (tmpbuf != NULL) { @@ -531,12 +537,6 @@ static int buffer_encrypt_record(ptls_buffer_t *buf, size_t rec_start, struct st return ret; } -#define buffer_push_record(buf, type, block) \ - do { \ - ptls_buffer_push((buf), (type), PTLS_RECORD_VERSION_MAJOR, PTLS_RECORD_VERSION_MINOR); \ - ptls_buffer_push_block((buf), 2, block); \ - } while (0) - #define buffer_push_handshake_core(buf, key_sched, type, mess_start, block) \ do { \ ptls_buffer_push((buf), (type)); \ @@ -3214,28 +3214,10 @@ int ptls_receive(ptls_t *tls, ptls_buffer_t *decryptbuf, const void *_input, siz return ret; } -int ptls_send(ptls_t *tls, ptls_buffer_t *sendbuf, const void *_input, size_t inlen) +int ptls_send(ptls_t *tls, ptls_buffer_t *sendbuf, const void *input, size_t inlen) { - const uint8_t *input = (const uint8_t *)_input; - size_t pt_size; - int ret = 0; - assert(tls->traffic_protection.enc.aead != NULL); - - for (; inlen != 0; input += pt_size, inlen -= pt_size) { - pt_size = inlen; - if (pt_size > PTLS_MAX_PLAINTEXT_RECORD_SIZE) - pt_size = PTLS_MAX_PLAINTEXT_RECORD_SIZE; - buffer_push_record(sendbuf, PTLS_CONTENT_TYPE_APPDATA, { - if ((ret = ptls_buffer_reserve(sendbuf, pt_size + tls->traffic_protection.enc.aead->algo->tag_size + 1)) != 0) - goto Exit; - sendbuf->off += - aead_encrypt(&tls->traffic_protection.enc, sendbuf->base + sendbuf->off, input, pt_size, PTLS_CONTENT_TYPE_APPDATA); - }); - } - -Exit: - return ret; + return buffer_push_encrypted_records(sendbuf, PTLS_CONTENT_TYPE_APPDATA, input, inlen, &tls->traffic_protection.enc); } size_t ptls_get_record_overhead(ptls_t *tls)