Skip to content

Commit

Permalink
Merge pull request h2o#39 from h2o/master
Browse files Browse the repository at this point in the history
Align 2019/03/27
  • Loading branch information
huitema authored Mar 27, 2019
2 parents abfc36a + 7898a8d commit 4245a48
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 39 deletions.
2 changes: 1 addition & 1 deletion deps/picotest
Submodule picotest updated 2 files
+25 −17 picotest.c
+7 −0 picotest.h
3 changes: 2 additions & 1 deletion lib/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,7 @@ static int cipher_setup_crypto(ptls_cipher_context_t *_ctx, int is_enc, const vo
} else {
if (!EVP_DecryptInit_ex(ctx->evp, cipher, NULL, key, NULL))
goto Error;
EVP_CIPHER_CTX_set_padding(ctx->evp, 0); /* required to disable one block buffering in ECB mode */
}

return 0;
Expand All @@ -714,7 +715,7 @@ static void cipher_decrypt(ptls_cipher_context_t *_ctx, void *output, const void
struct cipher_context_t *ctx = (struct cipher_context_t *)_ctx;
int len = (int)_len, ret = EVP_DecryptUpdate(ctx->evp, output, &len, input, len);
assert(ret);
/* OpenSSL 1.1.0i (or d?) seems to set outlen to zero when passing a 16-byte input to aes128ecb // assert(len == (int)_len); */
assert(len == (int)_len);
}

static int aes128ecb_setup_crypto(ptls_cipher_context_t *ctx, int is_enc, const void *key)
Expand Down
86 changes: 58 additions & 28 deletions lib/picotls.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,10 @@ struct st_ptls_t {
uint8_t legacy_session_id[32];
ptls_key_exchange_context_t *key_share_ctx;
unsigned offered_psk : 1;
/**
* if 1-RTT write key is active
*/
unsigned using_early_data : 1;
unsigned early_data_skipped : 1;
struct st_ptls_certificate_request_t certificate_request;
} client;
struct {
Expand Down Expand Up @@ -356,7 +358,7 @@ struct st_ptls_extension_bitmap_t {
uint8_t bits[8]; /* only ids below 64 is tracked */
};

static uint8_t zeroes_of_max_digest_size[PTLS_MAX_DIGEST_SIZE] = {0};
static const uint8_t zeroes_of_max_digest_size[PTLS_MAX_DIGEST_SIZE] = {0};

static int hkdf_expand_label(ptls_hash_algorithm_t *algo, void *output, size_t outlen, ptls_iovec_t secret, const char *label,
ptls_iovec_t hash_value, const char *label_prefix);
Expand Down Expand Up @@ -1825,7 +1827,9 @@ static int send_client_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_
&resumption_ticket, &max_early_data_size, properties->client.session_ticket.base,
properties->client.session_ticket.base + properties->client.session_ticket.len) == 0) {
tls->client.offered_psk = 1;
tls->key_share = key_share;
/* key-share selected by HRR should not be overridden */
if (tls->key_share == NULL)
tls->key_share = key_share;
tls->cipher_suite = cipher_suite;
if (!is_second_flight && max_early_data_size != 0 && properties->client.max_early_data_size != NULL) {
tls->client.using_early_data = 1;
Expand Down Expand Up @@ -1952,34 +1956,34 @@ static int send_client_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_
}
if ((ret = push_additional_extensions(properties, sendbuf)) != 0)
goto Exit;
if (tls->ctx->save_ticket != NULL) {
if (tls->ctx->save_ticket != NULL || resumption_secret.base != NULL) {
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_PSK_KEY_EXCHANGE_MODES, {
ptls_buffer_push_block(sendbuf, 1, {
if (!tls->ctx->require_dhe_on_psk)
ptls_buffer_push(sendbuf, PTLS_PSK_KE_MODE_PSK);
ptls_buffer_push(sendbuf, PTLS_PSK_KE_MODE_PSK_DHE);
});
});
if (resumption_secret.base != NULL) {
if (tls->client.using_early_data && !is_second_flight)
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_EARLY_DATA, {});
/* pre-shared key "MUST be the last extension in the ClientHello" (draft-17 section 4.2.6) */
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_PRE_SHARED_KEY, {
ptls_buffer_push_block(sendbuf, 2, {
ptls_buffer_push_block(sendbuf, 2,
{ ptls_buffer_pushv(sendbuf, resumption_ticket.base, resumption_ticket.len); });
ptls_buffer_push32(sendbuf, obfuscated_ticket_age);
});
/* allocate space for PSK binder. the space is filled at the bottom of the function */
ptls_buffer_push_block(sendbuf, 2, {
ptls_buffer_push_block(sendbuf, 1, {
if ((ret = ptls_buffer_reserve(sendbuf, tls->key_schedule->hashes[0].algo->digest_size)) != 0)
goto Exit;
sendbuf->off += tls->key_schedule->hashes[0].algo->digest_size;
});
}
if (resumption_secret.base != NULL) {
if (tls->client.using_early_data && !is_second_flight)
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_EARLY_DATA, {});
/* pre-shared key "MUST be the last extension in the ClientHello" (draft-17 section 4.2.6) */
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_PRE_SHARED_KEY, {
ptls_buffer_push_block(sendbuf, 2, {
ptls_buffer_push_block(sendbuf, 2,
{ ptls_buffer_pushv(sendbuf, resumption_ticket.base, resumption_ticket.len); });
ptls_buffer_push32(sendbuf, obfuscated_ticket_age);
});
/* allocate space for PSK binder. the space is filled at the bottom of the function */
ptls_buffer_push_block(sendbuf, 2, {
ptls_buffer_push_block(sendbuf, 1, {
if ((ret = ptls_buffer_reserve(sendbuf, tls->key_schedule->hashes[0].algo->digest_size)) != 0)
goto Exit;
sendbuf->off += tls->key_schedule->hashes[0].algo->digest_size;
});
});
}
});
}
});
});
Expand All @@ -1997,6 +2001,7 @@ static int send_client_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_
ptls__key_schedule_update_hash(tls->key_schedule, emitter->buf->base + msghash_off, emitter->buf->off - msghash_off);

if (tls->client.using_early_data) {
assert(!is_second_flight);
if ((ret = setup_traffic_protection(tls, 1, "c e traffic", 1, 0)) != 0)
goto Exit;
if ((ret = push_change_cipher_spec(tls, emitter->buf)) != 0)
Expand Down Expand Up @@ -2164,6 +2169,16 @@ static int handle_hello_retry_request(ptls_t *tls, ptls_message_emitter_t *emitt
tls->client.key_share_ctx->on_exchange(&tls->client.key_share_ctx, 1, NULL, ptls_iovec_init(NULL, 0));
tls->client.key_share_ctx = NULL;
}
if (tls->client.using_early_data) {
/* release traffic encryption key so that 2nd CH goes out in cleartext, but keep the epoch at 1 since we've already called
* derive-secret */
if (tls->ctx->update_traffic_key == NULL) {
assert(tls->traffic_protection.enc.aead != NULL);
ptls_aead_free(tls->traffic_protection.enc.aead);
tls->traffic_protection.enc.aead = NULL;
}
tls->client.using_early_data = 0;
}

if (sh->retry_request.selected_group != UINT16_MAX) {
/* we offer the first key_exchanges[0] as KEY_SHARE unless client.negotiate_before_key_exchange is set */
Expand Down Expand Up @@ -2356,7 +2371,8 @@ static int client_handle_encrypted_extensions(ptls_t *tls, ptls_iovec_t message,
}

if (tls->client.using_early_data) {
tls->client.early_data_skipped = skip_early_data;
if (skip_early_data)
tls->client.using_early_data = 0;
if (properties != NULL)
properties->client.early_data_acceptance = skip_early_data ? PTLS_EARLY_DATA_REJECTED : PTLS_EARLY_DATA_ACCEPTED;
}
Expand Down Expand Up @@ -2733,8 +2749,9 @@ static int client_handle_finished(ptls_t *tls, ptls_message_emitter_t *emitter,
/* if sending early data, emit EOED and commision the client handshake traffic secret */
if (tls->pending_handshake_secret != NULL) {
assert(tls->traffic_protection.enc.aead != NULL || tls->ctx->update_traffic_key != NULL);
if (!tls->client.early_data_skipped && !tls->ctx->omit_end_of_early_data)
if (tls->client.using_early_data && !tls->ctx->omit_end_of_early_data)
ptls_push_message(emitter, tls->key_schedule, PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA, {});
tls->client.using_early_data = 0;
if ((ret = commission_handshake_secret(tls)) != 0)
goto Exit;
}
Expand Down Expand Up @@ -4933,21 +4950,34 @@ int ptls_is_server(ptls_t *tls)

struct st_ptls_raw_message_emitter_t {
ptls_message_emitter_t super;
size_t start_off;
size_t *epoch_offsets;
};

static int begin_raw_message(ptls_message_emitter_t *_self)
{
struct st_ptls_raw_message_emitter_t *self = (void *)_self;

self->start_off = self->super.buf->off;
return 0;
}

static int commit_raw_message(ptls_message_emitter_t *_self)
{
struct st_ptls_raw_message_emitter_t *self = (void *)_self;
size_t i;
size_t epoch;

/* epoch is the key epoch, with the only exception being 2nd CH generated after 0-RTT key */
epoch = self->super.enc->epoch;
if (epoch == 1 && self->super.buf->base[self->start_off] == PTLS_HANDSHAKE_TYPE_CLIENT_HELLO)
epoch = 0;

for (++epoch; epoch < 5; ++epoch) {
assert(self->epoch_offsets[epoch] == self->start_off);
self->epoch_offsets[epoch] = self->super.buf->off;
}

for (i = self->super.enc->epoch + 1; i < 5; ++i)
self->epoch_offsets[i] = self->super.buf->off;
self->start_off = SIZE_MAX;

return 0;
}
Expand Down Expand Up @@ -4986,7 +5016,7 @@ int ptls_handle_message(ptls_t *tls, ptls_buffer_t *sendbuf, size_t epoch_offset
size_t inlen, ptls_handshake_properties_t *properties)
{
struct st_ptls_raw_message_emitter_t emitter = {
{sendbuf, &tls->traffic_protection.enc, 0, begin_raw_message, commit_raw_message}, epoch_offsets};
{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 (input == NULL)
Expand Down
12 changes: 6 additions & 6 deletions t/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,20 +91,20 @@ static void test_bf(void)
static const uint8_t key[PTLS_BLOWFISH_KEY_SIZE] = {0},
plaintext[PTLS_BLOWFISH_BLOCK_SIZE] = {0x4e, 0xf9, 0x97, 0x45, 0x61, 0x98, 0xdd, 0x78},
expected[PTLS_BLOWFISH_BLOCK_SIZE] = {0xe1, 0xc0, 0x30, 0xe7, 0x4c, 0x14, 0xd2, 0x61};
uint8_t actual[PTLS_BLOWFISH_BLOCK_SIZE];
uint8_t encrypted[PTLS_BLOWFISH_BLOCK_SIZE], decrypted[PTLS_BLOWFISH_BLOCK_SIZE];

/* encrypt */
memset(actual, 0, sizeof(actual));
ptls_cipher_context_t *ctx = ptls_cipher_new(&ptls_openssl_bfecb, 1, key);
ptls_cipher_encrypt(ctx, actual, plaintext, sizeof(actual));
ptls_cipher_encrypt(ctx, encrypted, plaintext, PTLS_BLOWFISH_BLOCK_SIZE);
ptls_cipher_free(ctx);
ok(memcmp(actual, expected, sizeof(actual)) == 0);
ok(memcmp(encrypted, expected, PTLS_BLOWFISH_BLOCK_SIZE) == 0);

/* decrypt */
ctx = ptls_cipher_new(&ptls_openssl_bfecb, 0, key);
ptls_cipher_encrypt(ctx, actual, actual, sizeof(actual));
ptls_cipher_encrypt(ctx, decrypted, "deadbeef", PTLS_BLOWFISH_BLOCK_SIZE);
ptls_cipher_encrypt(ctx, decrypted, encrypted, PTLS_BLOWFISH_BLOCK_SIZE);
ptls_cipher_free(ctx);
ok(memcmp(actual, plaintext, sizeof(actual)) == 0);
ok(memcmp(decrypted, plaintext, PTLS_BLOWFISH_BLOCK_SIZE) == 0);
#endif
}

Expand Down
144 changes: 141 additions & 3 deletions t/picotls.c
Original file line number Diff line number Diff line change
Expand Up @@ -1207,14 +1207,15 @@ static void test_handshake_api(void)
memset(coffs, 0, sizeof(coffs));
memset(soffs, 0, sizeof(soffs));

ctx->save_ticket = NULL; /* don't allow further test to update the saved ticket */

/* 0-RTT resumption */
size_t max_early_data_size = 0;
ptls_handshake_properties_t client_hs_prop = {{{{NULL}, saved_ticket, &max_early_data_size}}};
client = ptls_new(ctx, 0);
*ptls_get_data_ptr(client) = &client_secrets;
server = ptls_new(ctx_peer, 1);
*ptls_get_data_ptr(server) = &server_secrets;

/* 0-RTT resumption */
ret = ptls_handle_message(client, &cbuf, coffs, 0, NULL, 0, &client_hs_prop);
ok(ret == PTLS_ERROR_IN_PROGRESS);
ok(max_early_data_size != 0);
Expand All @@ -1236,8 +1237,145 @@ static void test_handshake_api(void)
ok(memcmp(client_secrets[1][3], zeroes_of_max_digest_size, PTLS_MAX_DIGEST_SIZE) != 0);
ret = feed_messages(server, &sbuf, soffs, cbuf.base, coffs, NULL);
ok(ret == 0);
ok(sbuf.off == 0);
ok(ptls_handshake_is_complete(server));
ok(memcmp(client_secrets[1][3], server_secrets[0][3], PTLS_MAX_DIGEST_SIZE) == 0);
ok(memcmp(server_secrets[0][3], zeroes_of_max_digest_size, PTLS_MAX_DIGEST_SIZE) != 0);

ptls_free(client);
ptls_free(server);

cbuf.off = 0;
sbuf.off = 0;
memset(client_secrets, 0, sizeof(client_secrets));
memset(server_secrets, 0, sizeof(server_secrets));
memset(coffs, 0, sizeof(coffs));
memset(soffs, 0, sizeof(soffs));

/* 0-RTT rejection */
ctx_peer->max_early_data_size = 0;
client_hs_prop = (ptls_handshake_properties_t){{{{NULL}, saved_ticket, &max_early_data_size}}};
client = ptls_new(ctx, 0);
*ptls_get_data_ptr(client) = &client_secrets;
server = ptls_new(ctx_peer, 1);
*ptls_get_data_ptr(server) = &server_secrets;
ret = ptls_handle_message(client, &cbuf, coffs, 0, NULL, 0, &client_hs_prop);
ok(ret == PTLS_ERROR_IN_PROGRESS);
ok(max_early_data_size != 0);
ok(memcmp(client_secrets[1][1], zeroes_of_max_digest_size, PTLS_MAX_DIGEST_SIZE) != 0);
ret = feed_messages(server, &sbuf, soffs, cbuf.base, coffs, NULL);
ok(ret == 0);
ok(sbuf.off != 0);
ok(!ptls_handshake_is_complete(server));
ret = feed_messages(client, &cbuf, coffs, sbuf.base, soffs, &client_hs_prop);
ok(ret == 0);
ok(cbuf.off != 0);
ok(ptls_handshake_is_complete(client));
ok(client_hs_prop.client.early_data_acceptance == PTLS_EARLY_DATA_REJECTED);
ok(memcmp(server_secrets[0][1], zeroes_of_max_digest_size, PTLS_MAX_DIGEST_SIZE) == 0);
ret = feed_messages(server, &sbuf, soffs, cbuf.base, coffs, NULL);
ok(ret == 0);
ok(sbuf.off == 0);
ok(ptls_handshake_is_complete(server));

ptls_free(client);
ptls_free(server);

cbuf.off = 0;
sbuf.off = 0;
memset(client_secrets, 0, sizeof(client_secrets));
memset(server_secrets, 0, sizeof(server_secrets));
memset(coffs, 0, sizeof(coffs));
memset(soffs, 0, sizeof(soffs));

/* HRR rejects 0-RTT */
ctx_peer->max_early_data_size = 8192;
ptls_handshake_properties_t server_hs_prop = {{{{NULL}}}};
server_hs_prop.server.enforce_retry = 1;
client_hs_prop = (ptls_handshake_properties_t){{{{NULL}, saved_ticket, &max_early_data_size}}};
client = ptls_new(ctx, 0);
*ptls_get_data_ptr(client) = &client_secrets;
server = ptls_new(ctx_peer, 1);
*ptls_get_data_ptr(server) = &server_secrets;
ret = ptls_handle_message(client, &cbuf, coffs, 0, NULL, 0, &client_hs_prop); /* -> CH */
ok(ret == PTLS_ERROR_IN_PROGRESS);
ok(max_early_data_size != 0);
ok(memcmp(client_secrets[1][1], zeroes_of_max_digest_size, PTLS_MAX_DIGEST_SIZE) != 0);
ret = feed_messages(server, &sbuf, soffs, cbuf.base, coffs, &server_hs_prop); /* CH -> HRR */
ok(ret == PTLS_ERROR_IN_PROGRESS);
ok(sbuf.off != 0);
ok(!ptls_handshake_is_complete(server));
ret = feed_messages(client, &cbuf, coffs, sbuf.base, soffs, &client_hs_prop); /* HRR -> CH */
ok(ret == PTLS_ERROR_IN_PROGRESS);
ok(cbuf.off != 0);
ok(!ptls_handshake_is_complete(client));
ok(client_hs_prop.client.early_data_acceptance == PTLS_EARLY_DATA_REJECTED);
ret = feed_messages(server, &sbuf, soffs, cbuf.base, coffs, &server_hs_prop); /* CH -> SH..SF */
ok(ret == 0);
ok(!ptls_handshake_is_complete(server));
ok(memcmp(server_secrets[0][1], zeroes_of_max_digest_size, PTLS_MAX_DIGEST_SIZE) == 0);
ok(sbuf.off != 0);
ret = feed_messages(client, &cbuf, coffs, sbuf.base, soffs, &client_hs_prop); /* SH..SF -> CF */
ok(ret == 0);
ok(ptls_handshake_is_complete(client));
ok(cbuf.off != 0);
ret = feed_messages(server, &sbuf, soffs, cbuf.base, coffs, &server_hs_prop); /* CF -> */
ok(ret == 0);
ok(ptls_handshake_is_complete(server));

ptls_free(client);
ptls_free(server);

cbuf.off = 0;
sbuf.off = 0;

/* shamelessly reuse this subtest for testing ordinary TLS 0-RTT with HRR rejection */
ctx->update_traffic_key = NULL;
ctx->omit_end_of_early_data = 0;
ctx_peer->update_traffic_key = NULL;
ctx_peer->omit_end_of_early_data = 0;
client_hs_prop = (ptls_handshake_properties_t){{{{NULL}, saved_ticket, &max_early_data_size}}};
server_hs_prop = (ptls_handshake_properties_t){{{{NULL}}}};
server_hs_prop.server.enforce_retry = 1;
client = ptls_new(ctx, 0);
server = ptls_new(ctx_peer, 1);
ret = ptls_handshake(client, &cbuf, NULL, NULL, &client_hs_prop); /* -> CH */
ok(ret == PTLS_ERROR_IN_PROGRESS);
ok(client_hs_prop.client.max_early_data_size != 0);
ok(client_hs_prop.client.early_data_acceptance == PTLS_EARLY_DATA_ACCEPTANCE_UNKNOWN);
ok(cbuf.off != 0);
ret = ptls_send(client, &cbuf, "hello world", 11); /* send 0-RTT data that'll be rejected */
ok(ret == 0);
size_t inlen = cbuf.off;
ret = ptls_handshake(server, &sbuf, cbuf.base, &inlen, &server_hs_prop); /* CH -> HRR */
ok(ret == PTLS_ERROR_IN_PROGRESS);
ok(cbuf.off == inlen);
cbuf.off = 0;
ok(sbuf.off != 0);
inlen = sbuf.off;
ret = ptls_handshake(client, &cbuf, sbuf.base, &inlen, &client_hs_prop); /* HRR -> CH */
ok(ret == PTLS_ERROR_IN_PROGRESS);
ok(client_hs_prop.client.early_data_acceptance == PTLS_EARLY_DATA_REJECTED);
ok(sbuf.off == inlen);
sbuf.off = 0;
ok(cbuf.off != 0);
inlen = cbuf.off;
ret = ptls_handshake(server, &sbuf, cbuf.base, &inlen, &server_hs_prop); /* CH -> SH..SF,NST */
ok(ret == 0);
ok(!ptls_handshake_is_complete(server));
ok(cbuf.off == inlen);
cbuf.off = 0;
ok(sbuf.off != 0);
inlen = sbuf.off;
ret = ptls_handshake(client, &cbuf, sbuf.base, &inlen, &client_hs_prop); /* SH..SF -> CF */
ok(ret == 0);
ok(ptls_handshake_is_complete(client));
ok(inlen < sbuf.off); /* ignore NST */
sbuf.off = 0;
inlen = cbuf.off;
ret = ptls_handshake(server, &sbuf, cbuf.base, &inlen, &server_hs_prop); /* CF -> */
ok(ret == 0);
ok(ptls_handshake_is_complete(server));
ok(sbuf.off == 0);

ptls_free(client);
ptls_free(server);
Expand Down

0 comments on commit 4245a48

Please sign in to comment.