diff options
Diffstat (limited to 'web/server/h2o/libh2o/deps/picotls/lib/picotls.c')
-rw-r--r-- | web/server/h2o/libh2o/deps/picotls/lib/picotls.c | 3761 |
1 files changed, 0 insertions, 3761 deletions
diff --git a/web/server/h2o/libh2o/deps/picotls/lib/picotls.c b/web/server/h2o/libh2o/deps/picotls/lib/picotls.c deleted file mode 100644 index d328aaacc..000000000 --- a/web/server/h2o/libh2o/deps/picotls/lib/picotls.c +++ /dev/null @@ -1,3761 +0,0 @@ -/* - * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#include <assert.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#ifdef _WINDOWS -#include "wincompat.h" -#else -#include <sys/time.h> -#endif -#include "picotls.h" - -#define PTLS_MAX_PLAINTEXT_RECORD_SIZE 16384 -#define PTLS_MAX_ENCRYPTED_RECORD_SIZE (16384 + 256) - -#define PTLS_RECORD_VERSION_MAJOR 3 -#define PTLS_RECORD_VERSION_MINOR 3 - -#define PTLS_HELLO_RANDOM_SIZE 32 - -#define PTLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC 20 -#define PTLS_CONTENT_TYPE_ALERT 21 -#define PTLS_CONTENT_TYPE_HANDSHAKE 22 -#define PTLS_CONTENT_TYPE_APPDATA 23 - -#define PTLS_HANDSHAKE_TYPE_CLIENT_HELLO 1 -#define PTLS_HANDSHAKE_TYPE_SERVER_HELLO 2 -#define PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET 4 -#define PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA 5 -#define PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS 8 -#define PTLS_HANDSHAKE_TYPE_CERTIFICATE 11 -#define PTLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST 13 -#define PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY 15 -#define PTLS_HANDSHAKE_TYPE_FINISHED 20 -#define PTLS_HANDSHAKE_TYPE_KEY_UPDATE 24 -#define PTLS_HANDSHAKE_TYPE_MESSAGE_HASH 254 - -#define PTLS_PSK_KE_MODE_PSK 0 -#define PTLS_PSK_KE_MODE_PSK_DHE 1 - -#define PTLS_HANDSHAKE_HEADER_SIZE 4 - -#define PTLS_EXTENSION_TYPE_SERVER_NAME 0 -#define PTLS_EXTENSION_TYPE_STATUS_REQUEST 5 -#define PTLS_EXTENSION_TYPE_SUPPORTED_GROUPS 10 -#define PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS 13 -#define PTLS_EXTENSION_TYPE_ALPN 16 -#define PTLS_EXTENSION_TYPE_PRE_SHARED_KEY 41 -#define PTLS_EXTENSION_TYPE_EARLY_DATA 42 -#define PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS 43 -#define PTLS_EXTENSION_TYPE_COOKIE 44 -#define PTLS_EXTENSION_TYPE_PSK_KEY_EXCHANGE_MODES 45 -#define PTLS_EXTENSION_TYPE_KEY_SHARE 51 - -#define PTLS_PROTOCOL_VERSION_DRAFT26 0x7f1a -#define PTLS_PROTOCOL_VERSION_DRAFT27 0x7f1b -#define PTLS_PROTOCOL_VERSION_DRAFT28 0x7f1c - -#define PTLS_SERVER_NAME_TYPE_HOSTNAME 0 - -#define PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING "TLS 1.3, server CertificateVerify" -#define PTLS_CLIENT_CERTIFICATE_VERIFY_CONTEXT_STRING "TLS 1.3, client CertificateVerify" -#define PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE \ - (64 + sizeof(PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING) + PTLS_MAX_DIGEST_SIZE * 2) - -#define PTLS_EARLY_DATA_MAX_DELAY 10000 /* max. RTT (in msec) to permit early data */ - -#if defined(PTLS_DEBUG) && PTLS_DEBUG -#define PTLS_DEBUGF(...) fprintf(stderr, __VA_ARGS__) -#else -#define PTLS_DEBUGF(...) -#endif - -#ifndef PTLS_MEMORY_DEBUG -#define PTLS_MEMORY_DEBUG 0 -#endif - -/** - * list of supported versions in the preferred order - */ -static const uint16_t supported_versions[] = {PTLS_PROTOCOL_VERSION_DRAFT28, PTLS_PROTOCOL_VERSION_DRAFT27, - PTLS_PROTOCOL_VERSION_DRAFT26}; - -static const uint8_t hello_retry_random[PTLS_HELLO_RANDOM_SIZE] = {0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, - 0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, - 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C}; - -struct st_ptls_traffic_protection_t { - uint8_t secret[PTLS_MAX_DIGEST_SIZE]; - ptls_aead_context_t *aead; - uint64_t seq; -}; - -struct st_ptls_early_data_t { - uint8_t next_secret[PTLS_MAX_DIGEST_SIZE]; -}; - -struct st_ptls_t { - /** - * the context - */ - ptls_context_t *ctx; - /** - * the state - */ - enum { - PTLS_STATE_CLIENT_HANDSHAKE_START, - PTLS_STATE_CLIENT_EXPECT_SERVER_HELLO, - PTLS_STATE_CLIENT_EXPECT_SECOND_SERVER_HELLO, - PTLS_STATE_CLIENT_EXPECT_ENCRYPTED_EXTENSIONS, - PTLS_STATE_CLIENT_EXPECT_CERTIFICATE, - PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_VERIFY, - PTLS_STATE_CLIENT_EXPECT_FINISHED, - PTLS_STATE_SERVER_EXPECT_CLIENT_HELLO, - PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO, - /* ptls_send can be called if the state is below here */ - PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA, - PTLS_STATE_SERVER_EXPECT_FINISHED, - PTLS_STATE_POST_HANDSHAKE_MIN, - PTLS_STATE_CLIENT_POST_HANDSHAKE = PTLS_STATE_POST_HANDSHAKE_MIN, - PTLS_STATE_SERVER_POST_HANDSHAKE - } state; - /** - * receive buffers - */ - struct { - ptls_buffer_t rec; - ptls_buffer_t mess; - } recvbuf; - /** - * key schedule - */ - struct st_ptls_key_schedule_t *key_schedule; - /** - * values used for record protection - */ - struct { - struct st_ptls_traffic_protection_t dec; - struct st_ptls_traffic_protection_t enc; - } traffic_protection; - /** - * server-name passed using SNI - */ - char *server_name; - /** - * result of ALPN - */ - char *negotiated_protocol; - /** - * selected key-exchange - */ - ptls_key_exchange_algorithm_t *key_share; - /** - * selected cipher-suite - */ - ptls_cipher_suite_t *cipher_suite; - /** - * clienthello.random - */ - uint8_t client_random[PTLS_HELLO_RANDOM_SIZE]; - /* flags */ - unsigned is_server : 1; - unsigned is_psk_handshake : 1; - unsigned skip_early_data : 1; /* if early-data is not recognized by the server */ - unsigned send_change_cipher_spec : 1; - /** - * exporter master secret (either 0rtt or 1rtt) - */ - struct { - uint8_t *early; - uint8_t *one_rtt; - } exporter_master_secret; - /** - * misc. - */ - union { - struct { - uint8_t legacy_session_id[16]; - ptls_key_exchange_context_t *key_share_ctx; - struct { - int (*cb)(void *verify_ctx, ptls_iovec_t data, ptls_iovec_t signature); - void *verify_ctx; - } certificate_verify; - unsigned offered_psk : 1; - } client; - struct { - uint8_t pending_traffic_secret[PTLS_MAX_DIGEST_SIZE]; - } server; - }; - /** - * the value contains the traffic secret to be commisioned after END_OF_EARLY_DATA - * END_OF_EARLY_DATA - */ - struct st_ptls_early_data_t *early_data; - /** - * user data - */ - void *data_ptr; -}; - -struct st_ptls_record_t { - uint8_t type; - uint16_t version; - size_t length; - const uint8_t *fragment; -}; - -struct st_ptls_client_hello_psk_t { - ptls_iovec_t identity; - uint32_t obfuscated_ticket_age; - ptls_iovec_t binder; -}; - -#define MAX_UNKNOWN_EXTENSIONS 16 - -struct st_ptls_client_hello_t { - const uint8_t *random_bytes; - ptls_iovec_t legacy_session_id; - struct { - const uint8_t *ids; - size_t count; - } compression_methods; - uint16_t selected_version; - ptls_iovec_t cipher_suites; - ptls_iovec_t negotiated_groups; - ptls_iovec_t key_shares; - struct { - uint16_t list[16]; /* expand? */ - size_t count; - } signature_algorithms; - ptls_iovec_t server_name; - struct { - ptls_iovec_t list[16]; - size_t count; - } alpn; - struct { - ptls_iovec_t all; - 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; - struct { - struct st_ptls_client_hello_psk_t list[4]; - size_t count; - } identities; - unsigned ke_modes; - int early_data_indication; - } psk; - ptls_raw_extension_t unknown_extensions[MAX_UNKNOWN_EXTENSIONS + 1]; - unsigned status_request : 1; -}; - -struct st_ptls_server_hello_t { - uint8_t random_[PTLS_HELLO_RANDOM_SIZE]; - ptls_iovec_t legacy_session_id; - int is_retry_request; - union { - ptls_iovec_t peerkey; - struct { - uint16_t selected_group; - ptls_iovec_t cookie; - } retry_request; - }; -}; - -struct st_ptls_key_schedule_t { - unsigned generation; /* early secret (1), hanshake secret (2), master secret (3) */ - uint8_t secret[PTLS_MAX_DIGEST_SIZE]; - size_t num_hashes; - struct { - ptls_hash_algorithm_t *algo; - ptls_hash_context_t *ctx; - } hashes[1]; -}; - -struct st_ptls_extension_decoder_t { - uint16_t type; - int (*cb)(ptls_t *tls, void *arg, const uint8_t *src, const uint8_t *const end); -}; - -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 int is_supported_version(uint16_t v) -{ - size_t i; - for (i = 0; i != sizeof(supported_versions) / sizeof(supported_versions[0]); ++i) - if (supported_versions[i] == v) - return 1; - return 0; -} - -static inline int extension_bitmap_is_set(struct st_ptls_extension_bitmap_t *bitmap, uint16_t id) -{ - if (id < sizeof(bitmap->bits) * 8) - return (bitmap->bits[id / 8] & (1 << (id % 8))) != 0; - return 0; -} - -static inline void extension_bitmap_set(struct st_ptls_extension_bitmap_t *bitmap, uint16_t id) -{ - if (id < sizeof(bitmap->bits) * 8) - bitmap->bits[id / 8] |= 1 << (id % 8); -} - -static inline void init_extension_bitmap(struct st_ptls_extension_bitmap_t *bitmap, uint8_t hstype) -{ - *bitmap = (struct st_ptls_extension_bitmap_t){{0}}; - -#define EXT(extid, proc) \ - do { \ - int _found = 0; \ - do { \ - proc \ - } while (0); \ - if (!_found) \ - extension_bitmap_set(bitmap, PTLS_EXTENSION_TYPE_##extid); \ - } while (0) -#define ALLOW(allowed_hstype) _found = _found || hstype == PTLS_HANDSHAKE_TYPE_##allowed_hstype - - /* Implements the table found in section 4.2 of draft-19; "If an implementation receives an extension which it recognizes and - * which is not specified for the message in which it appears it MUST abort the handshake with an “illegal_parameter” alert." - */ - EXT(SERVER_NAME, { - ALLOW(CLIENT_HELLO); - ALLOW(ENCRYPTED_EXTENSIONS); - }); - EXT(STATUS_REQUEST, { - ALLOW(CLIENT_HELLO); - ALLOW(CERTIFICATE); - }); - EXT(SUPPORTED_GROUPS, { - ALLOW(CLIENT_HELLO); - ALLOW(ENCRYPTED_EXTENSIONS); - }); - EXT(SIGNATURE_ALGORITHMS, { ALLOW(CLIENT_HELLO); }); - EXT(ALPN, { - ALLOW(CLIENT_HELLO); - ALLOW(ENCRYPTED_EXTENSIONS); - }); - EXT(KEY_SHARE, { - ALLOW(CLIENT_HELLO); - ALLOW(SERVER_HELLO); - }); - EXT(PRE_SHARED_KEY, { - ALLOW(CLIENT_HELLO); - ALLOW(SERVER_HELLO); - }); - EXT(PSK_KEY_EXCHANGE_MODES, { ALLOW(CLIENT_HELLO); }); - EXT(EARLY_DATA, { - ALLOW(CLIENT_HELLO); - ALLOW(ENCRYPTED_EXTENSIONS); - ALLOW(NEW_SESSION_TICKET); - }); - EXT(COOKIE, { - ALLOW(CLIENT_HELLO); - ALLOW(SERVER_HELLO); - }); - EXT(SUPPORTED_VERSIONS, { - ALLOW(CLIENT_HELLO); - ALLOW(SERVER_HELLO); - }); - -#undef ALLOW -#undef EXT -} - -static uint16_t ntoh16(const uint8_t *src) -{ - return (uint16_t)src[0] << 8 | src[1]; -} - -static uint32_t ntoh24(const uint8_t *src) -{ - return (uint32_t)src[0] << 16 | (uint32_t)src[1] << 8 | src[2]; -} - -static uint32_t ntoh32(const uint8_t *src) -{ - return (uint32_t)src[0] << 24 | (uint32_t)src[1] << 16 | (uint32_t)src[2] << 8 | src[3]; -} - -static uint64_t ntoh64(const uint8_t *src) -{ - return (uint64_t)src[0] << 56 | (uint64_t)src[1] << 48 | (uint64_t)src[2] << 40 | (uint64_t)src[3] << 32 | - (uint64_t)src[4] << 24 | (uint64_t)src[5] << 16 | (uint64_t)src[6] << 8 | src[7]; -} - -void ptls_buffer__release_memory(ptls_buffer_t *buf) -{ - ptls_clear_memory(buf->base, buf->off); - if (buf->is_allocated) - free(buf->base); -} - -int ptls_buffer_reserve(ptls_buffer_t *buf, size_t delta) -{ - if (buf->base == NULL) - return PTLS_ERROR_NO_MEMORY; - - if (PTLS_MEMORY_DEBUG || buf->capacity < buf->off + delta) { - uint8_t *newp; - size_t new_capacity = buf->capacity; - if (new_capacity < 1024) - new_capacity = 1024; - while (new_capacity < buf->off + delta) { - new_capacity *= 2; - } - if ((newp = malloc(new_capacity)) == NULL) - return PTLS_ERROR_NO_MEMORY; - memcpy(newp, buf->base, buf->off); - ptls_buffer__release_memory(buf); - buf->base = newp; - buf->capacity = new_capacity; - buf->is_allocated = 1; - } - - return 0; -} - -int ptls_buffer__do_pushv(ptls_buffer_t *buf, const void *src, size_t len) -{ - int ret; - - if (len == 0) - return 0; - if ((ret = ptls_buffer_reserve(buf, len)) != 0) - return ret; - memcpy(buf->base + buf->off, src, len); - buf->off += len; - return 0; -} - -int ptls_buffer__adjust_asn1_blocksize(ptls_buffer_t *buf, size_t body_size) -{ - fprintf(stderr, "unimplemented\n"); - abort(); -} - -int ptls_buffer_push_asn1_ubigint(ptls_buffer_t *buf, const void *bignum, size_t size) -{ - const uint8_t *p = bignum, *const end = p + size; - int ret; - - /* skip zeroes */ - for (; end - p >= 1; ++p) - if (*p != 0) - break; - - /* emit */ - ptls_buffer_push(buf, 2); - ptls_buffer_push_asn1_block(buf, { - if (*p >= 0x80) - ptls_buffer_push(buf, 0); - if (p != end) { - ptls_buffer_pushv(buf, p, end - p); - } else { - ptls_buffer_pushv(buf, "", 1); - } - }); - ret = 0; - -Exit: - return ret; -} - -static void build_aad(uint8_t aad[5], size_t reclen) -{ - aad[0] = PTLS_CONTENT_TYPE_APPDATA; - aad[1] = PTLS_RECORD_VERSION_MAJOR; - aad[2] = PTLS_RECORD_VERSION_MINOR; - aad[3] = (uint8_t)(reclen >> 8); - aad[4] = (uint8_t)reclen; -} - -static size_t aead_encrypt(struct st_ptls_traffic_protection_t *ctx, void *output, const void *input, size_t inlen, - uint8_t content_type) -{ - uint8_t aad[5]; - size_t off = 0; - - build_aad(aad, inlen + 1 + ctx->aead->algo->tag_size); - ptls_aead_encrypt_init(ctx->aead, ctx->seq++, aad, sizeof(aad)); - off += ptls_aead_encrypt_update(ctx->aead, ((uint8_t *)output) + off, input, inlen); - off += ptls_aead_encrypt_update(ctx->aead, ((uint8_t *)output) + off, &content_type, 1); - off += ptls_aead_encrypt_final(ctx->aead, ((uint8_t *)output) + off); - - return off; -} - -static int aead_decrypt(struct st_ptls_traffic_protection_t *ctx, void *output, size_t *outlen, const void *input, size_t inlen) -{ - uint8_t aad[5]; - - build_aad(aad, inlen); - if ((*outlen = ptls_aead_decrypt(ctx->aead, output, input, inlen, ctx->seq, aad, sizeof(aad))) == SIZE_MAX) - return PTLS_ALERT_BAD_RECORD_MAC; - ++ctx->seq; - return 0; -} - -#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 = 0; - - 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; - } - -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; - uint8_t *tmpbuf, type = buf->base[rec_start]; - int ret; - - /* fast path: do in-place encryption if only one record needs to be emitted */ - if (bodylen <= PTLS_MAX_PLAINTEXT_RECORD_SIZE) { - 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; - } - - /* 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); - ptls_clear_memory(buf->base + rec_start, bodylen + 5); - buf->off = rec_start; - - /* push encrypted records */ - ret = buffer_push_encrypted_records(buf, type, tmpbuf, bodylen, enc); - -Exit: - if (tmpbuf != NULL) { - ptls_clear_memory(tmpbuf, bodylen); - free(tmpbuf); - } - return ret; -} - -#define buffer_push_handshake_body(buf, key_sched, type, block) \ - do { \ - size_t mess_start = (buf)->off; \ - ptls_buffer_push((buf), (type)); \ - ptls_buffer_push_block((buf), 3, { \ - do { \ - block \ - } while (0); \ - }); \ - if ((key_sched) != NULL) \ - key_schedule_update_hash((key_sched), (buf)->base + (mess_start), (buf)->off - (mess_start)); \ - } while (0) - -#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_body((buf), (key_sched), (type), block); }); \ - if ((enc) != NULL) { \ - if ((ret = buffer_encrypt_record((buf), rec_start, (enc))) != 0) \ - goto Exit; \ - } \ - } while (0) - -#define buffer_push_extension(buf, type, block) \ - do { \ - ptls_buffer_push16((buf), (type)); \ - ptls_buffer_push_block((buf), 2, block); \ - } while (0); - -#define decode_open_extensions(src, end, hstype, exttype, block) \ - do { \ - struct st_ptls_extension_bitmap_t bitmap; \ - init_extension_bitmap(&bitmap, (hstype)); \ - ptls_decode_open_block((src), end, 2, { \ - while ((src) != end) { \ - if ((ret = ptls_decode16((exttype), &(src), end)) != 0) \ - goto Exit; \ - if (extension_bitmap_is_set(&bitmap, *(exttype)) != 0) { \ - ret = PTLS_ALERT_ILLEGAL_PARAMETER; \ - goto Exit; \ - } \ - extension_bitmap_set(&bitmap, *(exttype)); \ - ptls_decode_open_block((src), end, 2, block); \ - } \ - }); \ - } while (0) - -#define decode_extensions(src, end, hstype, exttype, block) \ - do { \ - decode_open_extensions((src), end, hstype, exttype, block); \ - ptls_decode_assert_block_close((src), end); \ - } while (0) - -int ptls_decode16(uint16_t *value, const uint8_t **src, const uint8_t *end) -{ - if (end - *src < 2) - return PTLS_ALERT_DECODE_ERROR; - *value = ntoh16(*src); - *src += 2; - return 0; -} - -int ptls_decode32(uint32_t *value, const uint8_t **src, const uint8_t *end) -{ - if (end - *src < 4) - return PTLS_ALERT_DECODE_ERROR; - *value = ntoh32(*src); - *src += 4; - return 0; -} - -int ptls_decode64(uint64_t *value, const uint8_t **src, const uint8_t *end) -{ - if (end - *src < 8) - return PTLS_ALERT_DECODE_ERROR; - *value = ntoh64(*src); - *src += 8; - return 0; -} - -static void key_schedule_free(struct st_ptls_key_schedule_t *sched) -{ - size_t i; - ptls_clear_memory(sched->secret, sizeof(sched->secret)); - for (i = 0; i != sched->num_hashes; ++i) - sched->hashes[i].ctx->final(sched->hashes[i].ctx, NULL, PTLS_HASH_FINAL_MODE_FREE); - free(sched); -} - -static struct st_ptls_key_schedule_t *key_schedule_new(ptls_cipher_suite_t *preferred, ptls_cipher_suite_t **offered) -{ -#define FOREACH_HASH(block) \ - do { \ - ptls_cipher_suite_t *cs; \ - if ((cs = preferred) != NULL) { \ - block \ - } \ - if (offered != NULL) { \ - size_t i, j; \ - for (i = 0; (cs = offered[i]) != NULL; ++i) { \ - if (preferred == NULL || cs->hash != preferred->hash) { \ - for (j = 0; j != i; ++j) \ - if (cs->hash == offered[j]->hash) \ - break; \ - if (j == i) { \ - block \ - } \ - } \ - } \ - } \ - } while (0) - - struct st_ptls_key_schedule_t *sched; - - { /* allocate */ - size_t num_hashes = 0; - FOREACH_HASH({ ++num_hashes; }); - if ((sched = malloc(offsetof(struct st_ptls_key_schedule_t, hashes) + sizeof(sched->hashes[0]) * num_hashes)) == NULL) - return NULL; - *sched = (struct st_ptls_key_schedule_t){0}; - } - - /* setup the hash algos and contexts */ - FOREACH_HASH({ - sched->hashes[sched->num_hashes].algo = cs->hash; - if ((sched->hashes[sched->num_hashes].ctx = cs->hash->create()) == NULL) - goto Fail; - ++sched->num_hashes; - }); - - return sched; -Fail: - key_schedule_free(sched); - return NULL; - -#undef FOREACH_HASH -} - -static int key_schedule_extract(struct st_ptls_key_schedule_t *sched, ptls_iovec_t ikm) -{ - int ret; - - if (ikm.base == NULL) - ikm = ptls_iovec_init(zeroes_of_max_digest_size, sched->hashes[0].algo->digest_size); - - if (sched->generation != 0 && - (ret = ptls_hkdf_expand_label(sched->hashes[0].algo, sched->secret, sched->hashes[0].algo->digest_size, - ptls_iovec_init(sched->secret, sched->hashes[0].algo->digest_size), "derived", - ptls_iovec_init(sched->hashes[0].algo->empty_digest, sched->hashes[0].algo->digest_size), - NULL)) != 0) - return ret; - - ++sched->generation; - ret = ptls_hkdf_extract(sched->hashes[0].algo, sched->secret, - ptls_iovec_init(sched->secret, sched->hashes[0].algo->digest_size), ikm); - PTLS_DEBUGF("%s: %u, %02x%02x\n", __FUNCTION__, sched->generation, (int)sched->secret[0], (int)sched->secret[1]); - return ret; -} - -static int key_schedule_select_one(struct st_ptls_key_schedule_t *sched, ptls_cipher_suite_t *cs, int reset) -{ - size_t found_slot = SIZE_MAX, i; - int ret; - - assert(sched->generation == 1); - - /* find the one, while freeing others */ - for (i = 0; i != sched->num_hashes; ++i) { - if (sched->hashes[i].algo == cs->hash) { - assert(found_slot == SIZE_MAX); - found_slot = i; - } else { - sched->hashes[i].ctx->final(sched->hashes[i].ctx, NULL, PTLS_HASH_FINAL_MODE_FREE); - } - } - if (found_slot != 0) { - sched->hashes[0] = sched->hashes[found_slot]; - reset = 1; - } - sched->num_hashes = 1; - - /* recalculate the hash if a different hash as been selected than the one we used for calculating the early secrets */ - if (reset) { - --sched->generation; - memset(sched->secret, 0, sizeof(sched->secret)); - if ((ret = key_schedule_extract(sched, ptls_iovec_init(NULL, 0))) != 0) - goto Exit; - } - - ret = 0; -Exit: - return ret; -} - -static void key_schedule_update_hash(struct st_ptls_key_schedule_t *sched, const uint8_t *msg, size_t msglen) -{ - size_t i; - - PTLS_DEBUGF("%s:%zu\n", __FUNCTION__, msglen); - for (i = 0; i != sched->num_hashes; ++i) - sched->hashes[i].ctx->update(sched->hashes[i].ctx, msg, msglen); -} - -static void key_schedule_update_ch1hash_prefix(struct st_ptls_key_schedule_t *sched) -{ - uint8_t prefix[4] = {PTLS_HANDSHAKE_TYPE_MESSAGE_HASH, 0, 0, (uint8_t)sched->hashes[0].algo->digest_size}; - key_schedule_update_hash(sched, prefix, sizeof(prefix)); -} - -static void key_schedule_extract_ch1hash(struct st_ptls_key_schedule_t *sched, uint8_t *hash) -{ - sched->hashes[0].ctx->final(sched->hashes[0].ctx, hash, PTLS_HASH_FINAL_MODE_RESET); -} - -static void key_schedule_transform_post_ch1hash(struct st_ptls_key_schedule_t *sched) -{ - uint8_t ch1hash[PTLS_MAX_DIGEST_SIZE]; - - key_schedule_extract_ch1hash(sched, ch1hash); - - key_schedule_update_ch1hash_prefix(sched); - key_schedule_update_hash(sched, ch1hash, sched->hashes[0].algo->digest_size); -} - -static int derive_secret_with_hash(struct st_ptls_key_schedule_t *sched, void *secret, const char *label, const uint8_t *hash) -{ - int ret = ptls_hkdf_expand_label(sched->hashes[0].algo, secret, sched->hashes[0].algo->digest_size, - ptls_iovec_init(sched->secret, sched->hashes[0].algo->digest_size), label, - ptls_iovec_init(hash, sched->hashes[0].algo->digest_size), NULL); - PTLS_DEBUGF("%s: (label=%s, hash=%02x%02x) => %02x%02x\n", __FUNCTION__, label, hash[0], hash[1], ((uint8_t *)secret)[0], - ((uint8_t *)secret)[1]); - return ret; -} - -static int derive_secret(struct st_ptls_key_schedule_t *sched, void *secret, const char *label) -{ - uint8_t hash_value[PTLS_MAX_DIGEST_SIZE]; - - sched->hashes[0].ctx->final(sched->hashes[0].ctx, hash_value, PTLS_HASH_FINAL_MODE_SNAPSHOT); - int ret = derive_secret_with_hash(sched, secret, label, hash_value); - ptls_clear_memory(hash_value, sizeof(hash_value)); - return ret; -} - -static int derive_secret_with_empty_digest(struct st_ptls_key_schedule_t *sched, void *secret, const char *label) -{ - return derive_secret_with_hash(sched, secret, label, sched->hashes[0].algo->empty_digest); -} - -static int derive_exporter_secret(ptls_t *tls, int is_early) -{ - if (tls->ctx->use_exporter) - return 0; - - uint8_t **slot = is_early ? &tls->exporter_master_secret.early : &tls->exporter_master_secret.one_rtt; - assert(*slot == NULL); - if ((*slot = malloc(tls->key_schedule->hashes[0].algo->digest_size)) == NULL) - return PTLS_ERROR_NO_MEMORY; - return derive_secret(tls->key_schedule, *slot, is_early ? "e exp master" : "exp master"); -} - -static void free_exporter_master_secret(ptls_t *tls, int is_early) -{ - uint8_t *slot = is_early ? tls->exporter_master_secret.early : tls->exporter_master_secret.one_rtt; - if (slot == NULL) - return; - assert(tls->key_schedule != NULL); - ptls_clear_memory(slot, tls->key_schedule->hashes[0].algo->digest_size); - free(slot); -} - -static int derive_resumption_secret(struct st_ptls_key_schedule_t *sched, uint8_t *secret, ptls_iovec_t nonce) -{ - int ret; - - if ((ret = derive_secret(sched, secret, "res master")) != 0) - goto Exit; - if ((ret = ptls_hkdf_expand_label(sched->hashes[0].algo, secret, sched->hashes[0].algo->digest_size, - ptls_iovec_init(secret, sched->hashes[0].algo->digest_size), "resumption", nonce, NULL)) != 0) - goto Exit; - -Exit: - if (ret != 0) - ptls_clear_memory(secret, sched->hashes[0].algo->digest_size); - return ret; -} - -static int decode_new_session_ticket(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) -{ - uint16_t exttype; - int ret; - - if ((ret = ptls_decode32(lifetime, &src, end)) != 0) - goto Exit; - if ((ret = ptls_decode32(age_add, &src, end)) != 0) - goto Exit; - ptls_decode_open_block(src, end, 1, { - *nonce = ptls_iovec_init(src, end - src); - src = end; - }); - ptls_decode_open_block(src, end, 2, { - if (src == end) { - ret = PTLS_ALERT_DECODE_ERROR; - goto Exit; - } - *ticket = ptls_iovec_init(src, end - src); - src = end; - }); - - *max_early_data_size = 0; - decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET, &exttype, { - switch (exttype) { - case PTLS_EXTENSION_TYPE_EARLY_DATA: - if ((ret = ptls_decode32(max_early_data_size, &src, end)) != 0) - goto Exit; - break; - default: - src = end; - break; - } - }); - - ret = 0; -Exit: - return ret; -} - -static int decode_stored_session_ticket(ptls_context_t *ctx, 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) -{ - uint16_t kxid, csid; - uint32_t lifetime, age_add; - uint64_t obtained_at, now; - ptls_iovec_t nonce; - int ret; - - /* decode */ - if ((ret = ptls_decode64(&obtained_at, &src, end)) != 0) - goto Exit; - if ((ret = ptls_decode16(&kxid, &src, end)) != 0) - goto Exit; - if ((ret = ptls_decode16(&csid, &src, end)) != 0) - goto Exit; - ptls_decode_open_block(src, end, 3, { - if ((ret = decode_new_session_ticket(&lifetime, &age_add, &nonce, ticket, max_early_data_size, src, end)) != 0) - goto Exit; - src = end; - }); - ptls_decode_block(src, end, 2, { - *secret = ptls_iovec_init(src, end - src); - src = end; - }); - - { /* determine the key-exchange */ - ptls_key_exchange_algorithm_t **cand; - for (cand = ctx->key_exchanges; *cand != NULL; ++cand) - if ((*cand)->id == kxid) - break; - if (*cand == NULL) { - ret = PTLS_ERROR_LIBRARY; - goto Exit; - } - *key_share = *cand; - } - - { /* determine the cipher-suite */ - ptls_cipher_suite_t **cand; - for (cand = ctx->cipher_suites; *cand != NULL; ++cand) - if ((*cand)->id == csid) - break; - if (*cand == NULL) { - ret = PTLS_ERROR_LIBRARY; - goto Exit; - } - *cs = *cand; - } - - /* calculate obfuscated_ticket_age */ - now = ctx->get_time->cb(ctx->get_time); - if (!(obtained_at <= now && now - obtained_at < 7 * 86400 * 1000)) { - ret = PTLS_ERROR_LIBRARY; - goto Exit; - } - *obfuscated_ticket_age = (uint32_t)(now - obtained_at) + age_add; - - ret = 0; -Exit: - return ret; -} - -static int get_traffic_key(ptls_hash_algorithm_t *algo, void *key, size_t key_size, int is_iv, const void *secret, - const char *base_label) -{ - return ptls_hkdf_expand_label(algo, key, key_size, ptls_iovec_init(secret, algo->digest_size), is_iv ? "iv" : "key", - ptls_iovec_init(NULL, 0), base_label); -} - -static int setup_traffic_protection(ptls_t *tls, int is_enc, const char *secret_label, const char *log_label) -{ - struct st_ptls_traffic_protection_t *ctx = is_enc ? &tls->traffic_protection.enc : &tls->traffic_protection.dec; - - if (secret_label != NULL) { - int ret; - if ((ret = derive_secret(tls->key_schedule, ctx->secret, secret_label)) != 0) - return ret; - } - - if (ctx->aead != NULL) - ptls_aead_free(ctx->aead); - if ((ctx->aead = ptls_aead_new(tls->cipher_suite->aead, tls->cipher_suite->hash, is_enc, ctx->secret, NULL)) == NULL) - return PTLS_ERROR_NO_MEMORY; /* TODO obtain error from ptls_aead_new */ - ctx->seq = 0; - - if (tls->ctx->log_secret != NULL) - tls->ctx->log_secret->cb(tls->ctx->log_secret, tls, log_label, - ptls_iovec_init(ctx->secret, tls->key_schedule->hashes[0].algo->digest_size)); - PTLS_DEBUGF("[%s] %02x%02x,%02x%02x\n", log_label, (unsigned)ctx->secret[0], (unsigned)ctx->secret[1], - (unsigned)ctx->aead->static_iv[0], (unsigned)ctx->aead->static_iv[1]); - - return 0; -} - -static int retire_early_data_secret(ptls_t *tls, int is_enc) -{ - assert(tls->early_data != NULL); - memcpy((is_enc ? &tls->traffic_protection.enc : &tls->traffic_protection.dec)->secret, tls->early_data->next_secret, - PTLS_MAX_DIGEST_SIZE); - ptls_clear_memory(tls->early_data, sizeof(*tls->early_data)); - free(tls->early_data); - tls->early_data = NULL; - - return setup_traffic_protection(tls, is_enc, NULL, "CLIENT_HANDSHAKE_TRAFFIC_SECRET"); -} - -#define SESSION_IDENTIFIER_MAGIC "ptls0001" /* the number should be changed upon incompatible format change */ -#define SESSION_IDENTIFIER_MAGIC_SIZE (sizeof(SESSION_IDENTIFIER_MAGIC) - 1) - -static int encode_session_identifier(ptls_context_t *ctx, ptls_buffer_t *buf, uint32_t ticket_age_add, ptls_iovec_t ticket_nonce, - struct st_ptls_key_schedule_t *sched, const char *server_name, uint16_t key_exchange_id, - uint16_t csid, const char *negotiated_protocol) -{ - int ret = 0; - - ptls_buffer_push_block(buf, 2, { - /* format id */ - ptls_buffer_pushv(buf, SESSION_IDENTIFIER_MAGIC, SESSION_IDENTIFIER_MAGIC_SIZE); - /* date */ - ptls_buffer_push64(buf, ctx->get_time->cb(ctx->get_time)); - /* resumption master secret */ - ptls_buffer_push_block(buf, 2, { - if ((ret = ptls_buffer_reserve(buf, sched->hashes[0].algo->digest_size)) != 0) - goto Exit; - if ((ret = derive_resumption_secret(sched, buf->base + buf->off, ticket_nonce)) != 0) - goto Exit; - buf->off += sched->hashes[0].algo->digest_size; - }); - /* key-exchange */ - ptls_buffer_push16(buf, key_exchange_id); - /* cipher-suite */ - ptls_buffer_push16(buf, csid); - /* ticket_age_add */ - ptls_buffer_push32(buf, ticket_age_add); - /* server-name */ - ptls_buffer_push_block(buf, 2, { - if (server_name != NULL) - ptls_buffer_pushv(buf, server_name, strlen(server_name)); - }); - /* alpn */ - ptls_buffer_push_block(buf, 1, { - if (negotiated_protocol != NULL) - ptls_buffer_pushv(buf, negotiated_protocol, strlen(negotiated_protocol)); - }); - }); - -Exit: - return ret; -} - -int decode_session_identifier(uint64_t *issued_at, ptls_iovec_t *psk, uint32_t *ticket_age_add, ptls_iovec_t *server_name, - uint16_t *key_exchange_id, uint16_t *csid, ptls_iovec_t *negotiated_protocol, const uint8_t *src, - const uint8_t *const end) -{ - int ret = 0; - - ptls_decode_block(src, end, 2, { - if (end - src < SESSION_IDENTIFIER_MAGIC_SIZE || - memcmp(src, SESSION_IDENTIFIER_MAGIC, SESSION_IDENTIFIER_MAGIC_SIZE) != 0) { - ret = PTLS_ALERT_DECODE_ERROR; - goto Exit; - } - src += SESSION_IDENTIFIER_MAGIC_SIZE; - if ((ret = ptls_decode64(issued_at, &src, end)) != 0) - goto Exit; - ptls_decode_open_block(src, end, 2, { - *psk = ptls_iovec_init(src, end - src); - src = end; - }); - if ((ret = ptls_decode16(key_exchange_id, &src, end)) != 0) - goto Exit; - if ((ret = ptls_decode16(csid, &src, end)) != 0) - goto Exit; - if ((ret = ptls_decode32(ticket_age_add, &src, end)) != 0) - goto Exit; - ptls_decode_open_block(src, end, 2, { - *server_name = ptls_iovec_init(src, end - src); - src = end; - }); - ptls_decode_open_block(src, end, 1, { - *negotiated_protocol = ptls_iovec_init(src, end - src); - src = end; - }); - }); - -Exit: - return ret; -} - -static size_t build_certificate_verify_signdata(uint8_t *data, struct st_ptls_key_schedule_t *sched, const char *context_string) -{ - size_t datalen = 0; - - memset(data + datalen, 32, 64); - datalen += 64; - memcpy(data + datalen, context_string, strlen(context_string) + 1); - datalen += strlen(context_string) + 1; - sched->hashes[0].ctx->final(sched->hashes[0].ctx, data + datalen, PTLS_HASH_FINAL_MODE_SNAPSHOT); - datalen += sched->hashes[0].algo->digest_size; - assert(datalen <= PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE); - - return datalen; -} - -static int calc_verify_data(void *output, struct st_ptls_key_schedule_t *sched, const void *secret) -{ - ptls_hash_context_t *hmac; - uint8_t digest[PTLS_MAX_DIGEST_SIZE]; - int ret; - - if ((ret = ptls_hkdf_expand_label(sched->hashes[0].algo, digest, sched->hashes[0].algo->digest_size, - ptls_iovec_init(secret, sched->hashes[0].algo->digest_size), "finished", - ptls_iovec_init(NULL, 0), NULL)) != 0) - return ret; - if ((hmac = ptls_hmac_create(sched->hashes[0].algo, digest, sched->hashes[0].algo->digest_size)) == NULL) { - ptls_clear_memory(digest, sizeof(digest)); - return PTLS_ERROR_NO_MEMORY; - } - - sched->hashes[0].ctx->final(sched->hashes[0].ctx, digest, PTLS_HASH_FINAL_MODE_SNAPSHOT); - PTLS_DEBUGF("%s: %02x%02x,%02x%02x\n", __FUNCTION__, ((uint8_t *)secret)[0], ((uint8_t *)secret)[1], digest[0], digest[1]); - hmac->update(hmac, digest, sched->hashes[0].algo->digest_size); - ptls_clear_memory(digest, sizeof(digest)); - hmac->final(hmac, output, PTLS_HASH_FINAL_MODE_FREE); - - return 0; -} - -static int verify_finished(ptls_t *tls, ptls_iovec_t message) -{ - uint8_t verify_data[PTLS_MAX_DIGEST_SIZE]; - int ret; - - if (PTLS_HANDSHAKE_HEADER_SIZE + tls->key_schedule->hashes[0].algo->digest_size != message.len) { - ret = PTLS_ALERT_DECODE_ERROR; - goto Exit; - } - - if ((ret = calc_verify_data(verify_data, tls->key_schedule, tls->traffic_protection.dec.secret)) != 0) - goto Exit; - if (memcmp(message.base + PTLS_HANDSHAKE_HEADER_SIZE, verify_data, tls->key_schedule->hashes[0].algo->digest_size) != 0) { - ret = PTLS_ALERT_HANDSHAKE_FAILURE; - goto Exit; - } - -Exit: - ptls_clear_memory(verify_data, sizeof(verify_data)); - return ret; -} - -static int send_finished(ptls_t *tls, ptls_buffer_t *sendbuf) -{ - int ret; - - buffer_push_handshake(sendbuf, tls->key_schedule, &tls->traffic_protection.enc, PTLS_HANDSHAKE_TYPE_FINISHED, { - if ((ret = ptls_buffer_reserve(sendbuf, tls->key_schedule->hashes[0].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->hashes[0].algo->digest_size; - }); - -Exit: - return ret; -} - -static int send_session_ticket(ptls_t *tls, ptls_buffer_t *sendbuf) -{ - ptls_hash_context_t *msghash_backup = tls->key_schedule->hashes[0].ctx->clone_(tls->key_schedule->hashes[0].ctx); - ptls_buffer_t session_id; - char session_id_smallbuf[128]; - uint32_t ticket_age_add; - 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 = sendbuf->off; - if (tls->early_data != NULL) { - assert(tls->state == PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA); - buffer_push_handshake_body(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA, {}); - sendbuf->off = orig_off; - } - buffer_push_handshake_body(sendbuf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_FINISHED, { - if ((ret = ptls_buffer_reserve(sendbuf, tls->key_schedule->hashes[0].algo->digest_size)) != 0) - goto Exit; - if ((ret = calc_verify_data(sendbuf->base + sendbuf->off, tls->key_schedule, - tls->early_data != NULL ? tls->early_data->next_secret - : tls->traffic_protection.dec.secret)) != 0) - goto Exit; - sendbuf->off += tls->key_schedule->hashes[0].algo->digest_size; - }); - sendbuf->off = orig_off; - } - - tls->ctx->random_bytes(&ticket_age_add, sizeof(ticket_age_add)); - - /* build the raw nsk */ - ptls_buffer_init(&session_id, session_id_smallbuf, sizeof(session_id_smallbuf)); - ret = encode_session_identifier(tls->ctx, &session_id, ticket_age_add, ptls_iovec_init(NULL, 0), tls->key_schedule, - tls->server_name, tls->key_share->id, tls->cipher_suite->id, tls->negotiated_protocol); - if (ret != 0) - goto Exit; - - /* encrypt and send */ - 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); }); - }); - }); - -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 push_change_cipher_spec(ptls_t *tls, ptls_buffer_t *sendbuf) -{ - int ret = 0; - - if (!tls->send_change_cipher_spec) - goto Exit; - buffer_push_record(sendbuf, PTLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, { ptls_buffer_push(sendbuf, 1); }); - tls->send_change_cipher_spec = 0; -Exit: - return ret; -} - -static int push_additional_extensions(ptls_handshake_properties_t *properties, ptls_buffer_t *sendbuf) -{ - int ret; - - if (properties != NULL && properties->additional_extensions != NULL) { - ptls_raw_extension_t *ext; - for (ext = properties->additional_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_client_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_handshake_properties_t *properties, ptls_iovec_t *cookie) -{ - ptls_iovec_t resumption_secret = {NULL}, resumption_ticket; - uint32_t obfuscated_ticket_age = 0; - size_t msghash_off; - uint8_t binder_key[PTLS_MAX_DIGEST_SIZE]; - int ret, is_second_flight = tls->key_schedule != NULL; - - if (properties != NULL) { - /* setup resumption-related data. If successful, resumption_secret becomes a non-zero value. */ - if (properties->client.session_ticket.base != NULL) { - ptls_key_exchange_algorithm_t *key_share = NULL; - ptls_cipher_suite_t *cipher_suite = NULL; - uint32_t max_early_data_size; - if (decode_stored_session_ticket(tls->ctx, &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) { - tls->client.offered_psk = 1; - 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) { - *properties->client.max_early_data_size = max_early_data_size; - if ((tls->early_data = malloc(sizeof(*tls->early_data))) == NULL) { - ret = PTLS_ERROR_NO_MEMORY; - goto Exit; - } - } - } else { - resumption_secret = ptls_iovec_init(NULL, 0); - } - } - if (properties->client.max_early_data_size != NULL && tls->early_data == NULL) - *properties->client.max_early_data_size = 0; - } - - /* use the default key share if still not undetermined */ - if (tls->key_share == NULL && !(properties != NULL && properties->client.negotiate_before_key_exchange)) - tls->key_share = tls->ctx->key_exchanges[0]; - - if (!is_second_flight) { - tls->key_schedule = key_schedule_new(tls->cipher_suite, tls->ctx->cipher_suites); - if ((ret = key_schedule_extract(tls->key_schedule, resumption_secret)) != 0) - goto Exit; - } - - msghash_off = sendbuf->off + 5; - buffer_push_handshake(sendbuf, NULL, NULL, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, { - /* legacy_version */ - ptls_buffer_push16(sendbuf, 0x0303); - /* random_bytes */ - ptls_buffer_pushv(sendbuf, tls->client_random, sizeof(tls->client_random)); - /* lecagy_session_id */ - ptls_buffer_push_block( - sendbuf, 1, { ptls_buffer_pushv(sendbuf, tls->client.legacy_session_id, sizeof(tls->client.legacy_session_id)); }); - /* cipher_suites */ - ptls_buffer_push_block(sendbuf, 2, { - ptls_cipher_suite_t **cs = tls->ctx->cipher_suites; - for (; *cs != NULL; ++cs) - ptls_buffer_push16(sendbuf, (*cs)->id); - }); - /* legacy_compression_methods */ - ptls_buffer_push_block(sendbuf, 1, { ptls_buffer_push(sendbuf, 0); }); - /* extensions */ - ptls_buffer_push_block(sendbuf, 2, { - if (tls->server_name != NULL) { - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_NAME, { - ptls_buffer_push_block(sendbuf, 2, { - ptls_buffer_push(sendbuf, PTLS_SERVER_NAME_TYPE_HOSTNAME); - ptls_buffer_push_block(sendbuf, 2, - { ptls_buffer_pushv(sendbuf, tls->server_name, strlen(tls->server_name)); }); - }); - }); - } - if (properties != NULL && properties->client.negotiated_protocols.count != 0) { - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ALPN, { - ptls_buffer_push_block(sendbuf, 2, { - size_t i; - for (i = 0; i != properties->client.negotiated_protocols.count; ++i) { - ptls_buffer_push_block(sendbuf, 1, { - ptls_iovec_t p = properties->client.negotiated_protocols.list[i]; - ptls_buffer_pushv(sendbuf, p.base, p.len); - }); - } - }); - }); - } - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS, { - ptls_buffer_push_block(sendbuf, 1, { - size_t i; - for (i = 0; i != sizeof(supported_versions) / sizeof(supported_versions[0]); ++i) - ptls_buffer_push16(sendbuf, supported_versions[i]); - }); - }); - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS, { - ptls_buffer_push_block(sendbuf, 2, { - ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256); - ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256); - ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_RSA_PKCS1_SHA256); - ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_RSA_PKCS1_SHA1); - }); - }); - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SUPPORTED_GROUPS, { - ptls_key_exchange_algorithm_t **algo = tls->ctx->key_exchanges; - ptls_buffer_push_block(sendbuf, 2, { - for (; *algo != NULL; ++algo) - ptls_buffer_push16(sendbuf, (*algo)->id); - }); - }); - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE, { - ptls_buffer_push_block(sendbuf, 2, { - if (tls->key_share != NULL) { - ptls_iovec_t pubkey; - if ((ret = tls->key_share->create(&tls->client.key_share_ctx, &pubkey)) != 0) - goto Exit; - ptls_buffer_push16(sendbuf, tls->key_share->id); - ptls_buffer_push_block(sendbuf, 2, { ptls_buffer_pushv(sendbuf, pubkey.base, pubkey.len); }); - } - }); - }); - if (cookie != NULL && cookie->base != NULL) { - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_COOKIE, { - ptls_buffer_push_block(sendbuf, 2, { ptls_buffer_pushv(sendbuf, cookie->base, cookie->len); }); - }); - } - if ((ret = push_additional_extensions(properties, sendbuf)) != 0) - goto Exit; - if (tls->ctx->save_ticket != 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->early_data != NULL && !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; - }); - }); - }); - } - } - }); - }); - - /* update the message hash, filling in the PSK binder HMAC if necessary */ - if (resumption_secret.base != NULL) { - size_t psk_binder_off = sendbuf->off - (3 + tls->key_schedule->hashes[0].algo->digest_size); - if ((ret = derive_secret_with_empty_digest(tls->key_schedule, binder_key, "res binder")) != 0) - goto Exit; - key_schedule_update_hash(tls->key_schedule, sendbuf->base + msghash_off, psk_binder_off - msghash_off); - msghash_off = psk_binder_off; - if ((ret = calc_verify_data(sendbuf->base + psk_binder_off + 3, tls->key_schedule, binder_key)) != 0) - goto Exit; - } - key_schedule_update_hash(tls->key_schedule, sendbuf->base + msghash_off, sendbuf->off - msghash_off); - - if (tls->early_data != NULL) { - if ((ret = setup_traffic_protection(tls, 1, "c e traffic", "CLIENT_EARLY_TRAFFIC_SECRET")) != 0) - goto Exit; - if ((ret = push_change_cipher_spec(tls, sendbuf)) != 0) - goto Exit; - } - if (resumption_secret.base != NULL && !is_second_flight) { - if ((ret = derive_exporter_secret(tls, 1)) != 0) - goto Exit; - } - tls->state = cookie == NULL ? PTLS_STATE_CLIENT_EXPECT_SERVER_HELLO : PTLS_STATE_CLIENT_EXPECT_SECOND_SERVER_HELLO; - ret = PTLS_ERROR_IN_PROGRESS; - -Exit: - ptls_clear_memory(binder_key, sizeof(binder_key)); - return ret; -} - -static int decode_key_share_entry(uint16_t *group, ptls_iovec_t *key_exchange, const uint8_t **src, const uint8_t *const end) -{ - int ret; - - if ((ret = ptls_decode16(group, src, end)) != 0) - goto Exit; - ptls_decode_open_block(*src, end, 2, { - *key_exchange = ptls_iovec_init(*src, end - *src); - *src = end; - }); - -Exit: - return ret; -} - -static ptls_cipher_suite_t *find_cipher_suite(ptls_context_t *ctx, uint16_t id) -{ - ptls_cipher_suite_t **cs; - - for (cs = ctx->cipher_suites; *cs != NULL && (*cs)->id != id; ++cs) - ; - return *cs; -} - -static int decode_server_hello(ptls_t *tls, struct st_ptls_server_hello_t *sh, const uint8_t *src, const uint8_t *const end) -{ - int ret; - - *sh = (struct st_ptls_server_hello_t){{0}}; - - /* ignore legacy-version */ - if (end - src < 2) { - ret = PTLS_ALERT_DECODE_ERROR; - goto Exit; - } - src += 2; - - /* random */ - if (end - src < PTLS_HELLO_RANDOM_SIZE) { - ret = PTLS_ALERT_DECODE_ERROR; - goto Exit; - } - sh->is_retry_request = memcmp(src, hello_retry_random, PTLS_HELLO_RANDOM_SIZE) == 0; - src += PTLS_HELLO_RANDOM_SIZE; - - /* legacy_session_id */ - ptls_decode_open_block(src, end, 1, { - if (end - src > 32) { - ret = PTLS_ALERT_DECODE_ERROR; - goto Exit; - } - sh->legacy_session_id = ptls_iovec_init(src, end - src); - src = end; - }); - - { /* select cipher_suite */ - uint16_t csid; - if ((ret = ptls_decode16(&csid, &src, end)) != 0) - goto Exit; - if ((tls->cipher_suite = find_cipher_suite(tls->ctx, csid)) == NULL) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - } - - /* legacy_compression_method */ - if (src == end || *src++ != 0) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - - if (sh->is_retry_request) - sh->retry_request.selected_group = UINT16_MAX; - - uint16_t exttype, found_version = UINT16_MAX, selected_psk_identity = UINT16_MAX; - decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_SERVER_HELLO, &exttype, { - switch (exttype) { - case PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS: - if ((ret = ptls_decode16(&found_version, &src, end)) != 0) - goto Exit; - break; - case PTLS_EXTENSION_TYPE_KEY_SHARE: - if (sh->is_retry_request) { - if ((ret = ptls_decode16(&sh->retry_request.selected_group, &src, end)) != 0) - goto Exit; - } else { - uint16_t group; - if ((ret = decode_key_share_entry(&group, &sh->peerkey, &src, end)) != 0) - goto Exit; - if (src != end) { - ret = PTLS_ALERT_DECODE_ERROR; - goto Exit; - } - if (tls->key_share == NULL || tls->key_share->id != group) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - } - break; - case PTLS_EXTENSION_TYPE_COOKIE: - if (sh->is_retry_request) { - ptls_decode_block(src, end, 2, { - if (src == end) { - ret = PTLS_ALERT_DECODE_ERROR; - goto Exit; - } - sh->retry_request.cookie = ptls_iovec_init(src, end - src); - src = end; - }); - } else { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - break; - case PTLS_EXTENSION_TYPE_PRE_SHARED_KEY: - if (sh->is_retry_request) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } else { - if ((ret = ptls_decode16(&selected_psk_identity, &src, end)) != 0) - goto Exit; - } - break; - default: - src = end; - break; - } - }); - - if (!is_supported_version(found_version)) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - if (!sh->is_retry_request) { - if (selected_psk_identity != UINT16_MAX) { - if (!tls->client.offered_psk) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - if (selected_psk_identity != 0) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - tls->is_psk_handshake = 1; - } - if (sh->peerkey.base == NULL && !tls->is_psk_handshake) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - } - - ret = 0; -Exit: - return ret; -} - -static int handle_hello_retry_request(ptls_t *tls, ptls_buffer_t *sendbuf, struct st_ptls_server_hello_t *sh, ptls_iovec_t message, - ptls_handshake_properties_t *properties) -{ - int ret; - - if (tls->client.key_share_ctx != NULL) { - tls->client.key_share_ctx->on_exchange(&tls->client.key_share_ctx, NULL, ptls_iovec_init(NULL, 0)); - tls->client.key_share_ctx = NULL; - } - - 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 */ - ptls_key_exchange_algorithm_t **cand; - for (cand = tls->ctx->key_exchanges; *cand != NULL; ++cand) - if ((*cand)->id == sh->retry_request.selected_group) - break; - if (*cand == NULL) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - tls->key_share = *cand; - } else if (tls->key_share != NULL) { - /* retain the key-share using in first CH, if server does not specify one */ - } else { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - - key_schedule_transform_post_ch1hash(tls->key_schedule); - key_schedule_update_hash(tls->key_schedule, message.base, message.len); - ret = send_client_hello(tls, sendbuf, properties, &sh->retry_request.cookie); - -Exit: - return ret; -} - -static int client_handle_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t message, ptls_handshake_properties_t *properties) -{ - struct st_ptls_server_hello_t sh; - ptls_iovec_t ecdh_secret = {NULL}; - int ret; - - if ((ret = decode_server_hello(tls, &sh, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len)) != 0) - goto Exit; - if (!(sh.legacy_session_id.len == sizeof(tls->client.legacy_session_id) && - memcmp(sh.legacy_session_id.base, tls->client.legacy_session_id, sizeof(tls->client.legacy_session_id)) == 0)) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - - if (sh.is_retry_request) { - if ((ret = key_schedule_select_one(tls->key_schedule, tls->cipher_suite, 0)) != 0) - goto Exit; - return handle_hello_retry_request(tls, sendbuf, &sh, message, properties); - } - - if ((ret = key_schedule_select_one(tls->key_schedule, tls->cipher_suite, tls->client.offered_psk && !tls->is_psk_handshake)) != - 0) - goto Exit; - - if (sh.peerkey.base != NULL) { - if ((ret = tls->client.key_share_ctx->on_exchange(&tls->client.key_share_ctx, &ecdh_secret, sh.peerkey)) != 0) - goto Exit; - } - - key_schedule_update_hash(tls->key_schedule, message.base, message.len); - - if ((ret = key_schedule_extract(tls->key_schedule, ecdh_secret)) != 0) - goto Exit; - if ((ret = setup_traffic_protection(tls, 0, "s hs traffic", "SERVER_HANDSHAKE_TRAFFIC_SECRET")) != 0) - goto Exit; - - tls->state = PTLS_STATE_CLIENT_EXPECT_ENCRYPTED_EXTENSIONS; - ret = PTLS_ERROR_IN_PROGRESS; - -Exit: - if (ecdh_secret.base != NULL) { - ptls_clear_memory(ecdh_secret.base, ecdh_secret.len); - free(ecdh_secret.base); - } - return ret; -} - -static int handle_unknown_extension(ptls_t *tls, ptls_handshake_properties_t *properties, uint16_t type, const uint8_t *src, - const uint8_t *const end, ptls_raw_extension_t *slots) -{ - - if (properties != NULL && properties->collect_extension != NULL && properties->collect_extension(tls, properties, type)) { - 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; - uint16_t type; - ptls_raw_extension_t unknown_extensions[MAX_UNKNOWN_EXTENSIONS + 1]; - int ret, skip_early_data = 1; - - unknown_extensions[0].type = UINT16_MAX; - - decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, &type, { - switch (type) { - case PTLS_EXTENSION_TYPE_SERVER_NAME: - if (src != end) { - ret = PTLS_ALERT_DECODE_ERROR; - goto Exit; - } - if (tls->server_name == NULL) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - break; - case PTLS_EXTENSION_TYPE_ALPN: - ptls_decode_block(src, end, 2, { - ptls_decode_open_block(src, end, 1, { - if ((ret = ptls_set_negotiated_protocol(tls, (const char *)src, end - src)) != 0) - goto Exit; - src = end; - }); - if (src != end) { - ret = PTLS_ALERT_HANDSHAKE_FAILURE; - goto Exit; - } - }); - break; - case PTLS_EXTENSION_TYPE_EARLY_DATA: - if (tls->early_data == NULL) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - skip_early_data = 0; - break; - default: - handle_unknown_extension(tls, properties, type, src, end, unknown_extensions); - break; - } - src = end; - }); - - if (tls->early_data != NULL) { - tls->skip_early_data = skip_early_data; - if (properties != NULL && !skip_early_data) - properties->client.early_data_accepted_by_peer = 1; - if ((ret = derive_secret(tls->key_schedule, tls->early_data->next_secret, "c hs traffic")) != 0) - goto Exit; - } else { - if ((ret = setup_traffic_protection(tls, 1, "c hs traffic", "CLIENT_HANDSHAKE_TRAFFIC_SECRET")) != 0) - goto Exit; - } - if ((ret = report_unknown_extensions(tls, properties, unknown_extensions)) != 0) - goto Exit; - - key_schedule_update_hash(tls->key_schedule, message.base, message.len); - tls->state = tls->is_psk_handshake ? PTLS_STATE_CLIENT_EXPECT_FINISHED : PTLS_STATE_CLIENT_EXPECT_CERTIFICATE; - ret = PTLS_ERROR_IN_PROGRESS; - -Exit: - return ret; -} - -static int client_handle_certificate(ptls_t *tls, ptls_iovec_t message) -{ - const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len; - ptls_iovec_t certs[16]; - size_t num_certs = 0; - int ret; - - /* certificate request context */ - ptls_decode_open_block(src, end, 1, { - if (src != end) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - }); - /* certificate_list */ - ptls_decode_block(src, end, 3, { - do { - ptls_decode_open_block(src, end, 3, { - if (num_certs < sizeof(certs) / sizeof(certs[0])) - certs[num_certs++] = ptls_iovec_init(src, end - src); - src = end; - }); - uint16_t type; - decode_open_extensions(src, end, PTLS_HANDSHAKE_TYPE_CERTIFICATE, &type, { src = end; }); - } while (src != end); - }); - - if (tls->ctx->verify_certificate != NULL) { - if ((ret = tls->ctx->verify_certificate->cb(tls->ctx->verify_certificate, tls, &tls->client.certificate_verify.cb, - &tls->client.certificate_verify.verify_ctx, certs, num_certs)) != 0) - goto Exit; - } - - key_schedule_update_hash(tls->key_schedule, message.base, message.len); - tls->state = PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_VERIFY; - ret = PTLS_ERROR_IN_PROGRESS; - -Exit: - return ret; -} - -static int client_handle_certificate_verify(ptls_t *tls, ptls_iovec_t message) -{ - const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len; - uint16_t algo; - ptls_iovec_t signature; - uint8_t signdata[PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE]; - size_t signdata_size; - int ret; - - /* decode */ - if ((ret = ptls_decode16(&algo, &src, end)) != 0) - goto Exit; - ptls_decode_block(src, end, 2, { - signature = ptls_iovec_init(src, end - src); - src = end; - }); - - /* validate */ - switch (algo) { - case PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256: - case PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256: - /* ok */ - break; - default: - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - signdata_size = build_certificate_verify_signdata(signdata, tls->key_schedule, PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING); - if (tls->client.certificate_verify.cb != NULL) { - ret = tls->client.certificate_verify.cb(tls->client.certificate_verify.verify_ctx, ptls_iovec_init(signdata, signdata_size), - signature); - } else { - ret = 0; - } - ptls_clear_memory(signdata, signdata_size); - tls->client.certificate_verify.cb = NULL; - if (ret != 0) - goto Exit; - - key_schedule_update_hash(tls->key_schedule, message.base, message.len); - tls->state = PTLS_STATE_CLIENT_EXPECT_FINISHED; - ret = PTLS_ERROR_IN_PROGRESS; - -Exit: - return ret; -} - -static int client_handle_finished(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t message) -{ - uint8_t send_secret[PTLS_MAX_DIGEST_SIZE]; - int ret; - - if ((ret = verify_finished(tls, message)) != 0) - goto Exit; - key_schedule_update_hash(tls->key_schedule, message.base, message.len); - - /* update traffic keys by using messages upto ServerFinished, but commission them after sending ClientFinished */ - if ((ret = key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0))) != 0) - goto Exit; - if ((ret = setup_traffic_protection(tls, 0, "s ap traffic", "SERVER_TRAFFIC_SECRET_0")) != 0) - goto Exit; - if ((ret = derive_secret(tls->key_schedule, send_secret, "c ap traffic")) != 0) - goto Exit; - if ((ret = derive_exporter_secret(tls, 0)) != 0) - goto Exit; - - /* if sending early data, emit EOED and commision the client handshake traffic secret */ - if (tls->early_data != NULL) { - assert(tls->traffic_protection.enc.aead != NULL); - if (!tls->skip_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; - } - - if ((ret = push_change_cipher_spec(tls, sendbuf)) != 0) - goto Exit; - ret = send_finished(tls, sendbuf); - - memcpy(tls->traffic_protection.enc.secret, send_secret, sizeof(send_secret)); - if ((ret = setup_traffic_protection(tls, 1, NULL, "CLIENT_TRAFFIC_SECRET_0")) != 0) - goto Exit; - - tls->state = PTLS_STATE_CLIENT_POST_HANDSHAKE; - -Exit: - ptls_clear_memory(send_secret, sizeof(send_secret)); - return ret; -} - -static int client_handle_new_session_ticket(ptls_t *tls, ptls_iovec_t message) -{ - const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len; - ptls_iovec_t ticket_nonce; - int ret; - - { /* verify the format */ - uint32_t ticket_lifetime, ticket_age_add, max_early_data_size; - ptls_iovec_t ticket; - if ((ret = decode_new_session_ticket(&ticket_lifetime, &ticket_age_add, &ticket_nonce, &ticket, &max_early_data_size, src, - end)) != 0) - return ret; - } - - /* do nothing if use of session ticket is disabled */ - if (tls->ctx->save_ticket == NULL) - return 0; - - /* save the extension, along with the key of myself */ - ptls_buffer_t ticket_buf; - uint8_t ticket_buf_small[512]; - ptls_buffer_init(&ticket_buf, ticket_buf_small, sizeof(ticket_buf_small)); - ptls_buffer_push64(&ticket_buf, tls->ctx->get_time->cb(tls->ctx->get_time)); - ptls_buffer_push16(&ticket_buf, tls->key_share->id); - ptls_buffer_push16(&ticket_buf, tls->cipher_suite->id); - ptls_buffer_push_block(&ticket_buf, 3, { ptls_buffer_pushv(&ticket_buf, src, end - src); }); - ptls_buffer_push_block(&ticket_buf, 2, { - if ((ret = ptls_buffer_reserve(&ticket_buf, tls->key_schedule->hashes[0].algo->digest_size)) != 0) - goto Exit; - if ((ret = derive_resumption_secret(tls->key_schedule, ticket_buf.base + ticket_buf.off, ticket_nonce)) != 0) - goto Exit; - ticket_buf.off += tls->key_schedule->hashes[0].algo->digest_size; - }); - - if ((ret = tls->ctx->save_ticket->cb(tls->ctx->save_ticket, tls, ptls_iovec_init(ticket_buf.base, ticket_buf.off))) != 0) - goto Exit; - - ret = 0; -Exit: - ptls_buffer_dispose(&ticket_buf); - return ret; -} - -static int client_hello_decode_server_name(ptls_iovec_t *name, const uint8_t *src, const uint8_t *const end) -{ - int ret = 0; - - ptls_decode_block(src, end, 2, { - if (src == end) { - ret = PTLS_ALERT_DECODE_ERROR; - goto Exit; - } - do { - uint8_t type = *src++; - ptls_decode_open_block(src, end, 2, { - switch (type) { - case PTLS_SERVER_NAME_TYPE_HOSTNAME: - if (memchr(src, '\0', end - src) != 0) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - *name = ptls_iovec_init(src, end - src); - break; - default: - break; - } - src = end; - }); - } while (src != end); - }); - -Exit: - return ret; -} - -static int select_cipher_suite(ptls_cipher_suite_t **selected, ptls_cipher_suite_t **candidates, const uint8_t *src, - const uint8_t *const end) -{ - int ret; - - ptls_decode_block(src, end, 2, { - while (src != end) { - uint16_t id; - if ((ret = ptls_decode16(&id, &src, end)) != 0) - goto Exit; - ptls_cipher_suite_t **c = candidates; - for (; *c != NULL; ++c) { - if ((*c)->id == id) { - *selected = *c; - return 0; - } - } - } - }); - - ret = PTLS_ALERT_HANDSHAKE_FAILURE; - -Exit: - return ret; -} - -static int select_key_share(ptls_key_exchange_algorithm_t **selected, ptls_iovec_t *peer_key, - ptls_key_exchange_algorithm_t **candidates, const uint8_t *src, const uint8_t *const end) -{ - int ret; - - ptls_decode_block(src, end, 2, { - while (src != end) { - uint16_t group; - ptls_iovec_t key; - if ((ret = decode_key_share_entry(&group, &key, &src, end)) != 0) - goto Exit; - ptls_key_exchange_algorithm_t **c = candidates; - for (; *c != NULL; ++c) { - if ((*c)->id == group) { - *selected = *c; - *peer_key = key; - return 0; - } - } - } - }); - - *selected = NULL; - ret = 0; - -Exit: - return ret; -} - -static int select_negotiated_group(ptls_key_exchange_algorithm_t **selected, ptls_key_exchange_algorithm_t **candidates, - const uint8_t *src, const uint8_t *const end) -{ - int ret; - - ptls_decode_block(src, end, 2, { - while (src != end) { - uint16_t group; - if ((ret = ptls_decode16(&group, &src, end)) != 0) - goto Exit; - ptls_key_exchange_algorithm_t **c = candidates; - for (; *c != NULL; ++c) { - if ((*c)->id == group) { - *selected = *c; - return 0; - } - } - } - }); - - ret = PTLS_ALERT_HANDSHAKE_FAILURE; - -Exit: - return ret; -} - -static int decode_client_hello(ptls_t *tls, struct st_ptls_client_hello_t *ch, const uint8_t *src, const uint8_t *const end, - ptls_handshake_properties_t *properties) -{ - uint16_t exttype = 0; - int ret; - - { /* check protocol version */ - uint16_t protver; - if ((ret = ptls_decode16(&protver, &src, end)) != 0) - goto Exit; - if (protver != 0x0303) { - ret = PTLS_ALERT_HANDSHAKE_FAILURE; - goto Exit; - } - } - - /* skip random */ - if (end - src < PTLS_HELLO_RANDOM_SIZE) { - ret = PTLS_ALERT_DECODE_ERROR; - goto Exit; - } - ch->random_bytes = src; - src += PTLS_HELLO_RANDOM_SIZE; - - /* skip legacy_session_id */ - ptls_decode_open_block(src, end, 1, { - if (end - src > 32) { - ret = PTLS_ALERT_DECODE_ERROR; - goto Exit; - } - ch->legacy_session_id = ptls_iovec_init(src, end - src); - src = end; - }); - - /* decode and select from ciphersuites */ - ptls_decode_open_block(src, end, 2, { - ch->cipher_suites = ptls_iovec_init(src - 2, end - src + 2); - src = end; - }); - - /* decode legacy_compression_methods */ - ptls_decode_open_block(src, end, 1, { - if (src == end) { - ret = PTLS_ALERT_DECODE_ERROR; - goto Exit; - } - ch->compression_methods.ids = src; - ch->compression_methods.count = end - src; - src = end; - }); - - /* decode extensions */ - decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, &exttype, { - switch (exttype) { - case PTLS_EXTENSION_TYPE_SERVER_NAME: - if ((ret = client_hello_decode_server_name(&ch->server_name, src, end)) != 0) - goto Exit; - break; - case PTLS_EXTENSION_TYPE_ALPN: - ptls_decode_block(src, end, 2, { - do { - ptls_decode_open_block(src, end, 1, { - if (ch->alpn.count < sizeof(ch->alpn.list) / sizeof(ch->alpn.list[0])) - ch->alpn.list[ch->alpn.count++] = ptls_iovec_init(src, end - src); - src = end; - }); - } while (src != end); - }); - break; - case PTLS_EXTENSION_TYPE_SUPPORTED_GROUPS: - ch->negotiated_groups = ptls_iovec_init(src, end - src); - break; - case PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS: - ptls_decode_block(src, end, 2, { - do { - uint16_t id; - if ((ret = ptls_decode16(&id, &src, end)) != 0) - goto Exit; - if (ch->signature_algorithms.count < - sizeof(ch->signature_algorithms.list) / sizeof(ch->signature_algorithms.list[0])) - ch->signature_algorithms.list[ch->signature_algorithms.count++] = id; - } while (src != end); - }); - break; - case PTLS_EXTENSION_TYPE_KEY_SHARE: - ch->key_shares = ptls_iovec_init(src, end - src); - break; - case PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS: - ptls_decode_block(src, end, 1, { - size_t selected_index = sizeof(supported_versions) / sizeof(supported_versions[0]); - do { - size_t i; - uint16_t v; - if ((ret = ptls_decode16(&v, &src, end)) != 0) - goto Exit; - for (i = 0; i != selected_index; ++i) { - if (supported_versions[i] == v) { - selected_index = i; - break; - } - } - } while (src != end); - if (selected_index != sizeof(supported_versions) / sizeof(supported_versions[0])) - ch->selected_version = supported_versions[selected_index]; - }); - break; - case PTLS_EXTENSION_TYPE_COOKIE: - if (properties->server.cookie.key == NULL) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - ch->cookie.all = ptls_iovec_init(src, end - src); - ptls_decode_block(src, end, 2, { - ch->cookie.tbs.base = (void *)src; - ptls_decode_open_block(src, end, 2, { - 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, { - ch->cookie.signature = ptls_iovec_init(src, end - src); - src = end; - }); - }); - break; - case PTLS_EXTENSION_TYPE_PRE_SHARED_KEY: { - size_t num_identities = 0; - ptls_decode_open_block(src, end, 2, { - do { - struct st_ptls_client_hello_psk_t psk = {{NULL}}; - ptls_decode_open_block(src, end, 2, { - psk.identity = ptls_iovec_init(src, end - src); - src = end; - }); - if ((ret = ptls_decode32(&psk.obfuscated_ticket_age, &src, end)) != 0) - goto Exit; - if (ch->psk.identities.count < sizeof(ch->psk.identities.list) / sizeof(ch->psk.identities.list[0])) - ch->psk.identities.list[ch->psk.identities.count++] = psk; - ++num_identities; - } while (src != end); - }); - ch->psk.hash_end = src; - ptls_decode_block(src, end, 2, { - size_t num_binders = 0; - do { - ptls_decode_open_block(src, end, 1, { - if (num_binders < ch->psk.identities.count) - ch->psk.identities.list[num_binders].binder = ptls_iovec_init(src, end - src); - src = end; - }); - ++num_binders; - } while (src != end); - if (num_identities != num_binders) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - }); - } break; - case PTLS_EXTENSION_TYPE_PSK_KEY_EXCHANGE_MODES: - ptls_decode_block(src, end, 1, { - if (src == end) { - ret = PTLS_ALERT_DECODE_ERROR; - goto Exit; - } - for (; src != end; ++src) { - if (*src < sizeof(ch->psk.ke_modes) * 8) - ch->psk.ke_modes |= 1u << *src; - } - }); - break; - case PTLS_EXTENSION_TYPE_EARLY_DATA: - ch->psk.early_data_indication = 1; - break; - case PTLS_EXTENSION_TYPE_STATUS_REQUEST: - ch->status_request = 1; - break; - default: - handle_unknown_extension(tls, properties, exttype, src, end, ch->unknown_extensions); - break; - } - src = end; - }); - - /* check if client hello make sense */ - if (is_supported_version(ch->selected_version)) { - if (!(ch->compression_methods.count == 1 && ch->compression_methods.ids[0] == 0)) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - /* pre-shared key */ - if (ch->psk.hash_end != NULL) { - /* PSK must be the last extension */ - if (exttype != PTLS_EXTENSION_TYPE_PRE_SHARED_KEY) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - } else { - if (ch->psk.early_data_indication) { - ret = PTLS_ALERT_ILLEGAL_PARAMETER; - goto Exit; - } - } - } else { - ret = PTLS_ALERT_PROTOCOL_VERSION; - goto Exit; - } - - ret = 0; -Exit: - return ret; -} - -static int vec_is_string(ptls_iovec_t x, const char *y) -{ - return strncmp((const char *)x.base, y, x.len) == 0 && y[x.len] == '\0'; -} - -static int try_psk_handshake(ptls_t *tls, size_t *psk_index, int *accept_early_data, struct st_ptls_client_hello_t *ch, - ptls_iovec_t ch_trunc) -{ - ptls_buffer_t decbuf; - ptls_iovec_t ticket_psk, ticket_server_name, ticket_negotiated_protocol; - uint64_t issue_at, now = tls->ctx->get_time->cb(tls->ctx->get_time); - uint32_t age_add; - uint16_t ticket_key_exchange_id, ticket_csid; - uint8_t decbuf_small[256], binder_key[PTLS_MAX_DIGEST_SIZE], verify_data[PTLS_MAX_DIGEST_SIZE]; - int ret; - - ptls_buffer_init(&decbuf, decbuf_small, sizeof(decbuf_small)); - - for (*psk_index = 0; *psk_index < ch->psk.identities.count; ++*psk_index) { - struct st_ptls_client_hello_psk_t *identity = ch->psk.identities.list + *psk_index; - /* decrypt and decode */ - decbuf.off = 0; - if ((tls->ctx->encrypt_ticket->cb(tls->ctx->encrypt_ticket, tls, 0, &decbuf, identity->identity)) != 0) - continue; - if (decode_session_identifier(&issue_at, &ticket_psk, &age_add, &ticket_server_name, &ticket_key_exchange_id, &ticket_csid, - &ticket_negotiated_protocol, decbuf.base, decbuf.base + decbuf.off) != 0) - continue; - /* check age */ - if (now < issue_at) - continue; - if (now - issue_at > (uint64_t)tls->ctx->ticket_lifetime * 1000) - continue; - *accept_early_data = 0; - if (ch->psk.early_data_indication) { - int64_t delta = (now - issue_at) - (identity->obfuscated_ticket_age - age_add); - if (delta <= PTLS_EARLY_DATA_MAX_DELAY) - *accept_early_data = 1; - } - /* check server-name */ - if (ticket_server_name.len != 0) { - if (tls->server_name == NULL) - continue; - if (!vec_is_string(ticket_server_name, tls->server_name)) - continue; - } else { - if (tls->server_name != NULL) - continue; - } - { /* check key-exchange */ - ptls_key_exchange_algorithm_t **a; - for (a = tls->ctx->key_exchanges; *a != NULL && (*a)->id != ticket_key_exchange_id; ++a) - ; - if (*a == NULL) - continue; - tls->key_share = *a; - } - /* check cipher-suite */ - if (ticket_csid != tls->cipher_suite->id) - continue; - /* check negotiated-protocol */ - if (ticket_negotiated_protocol.len != 0) { - if (tls->negotiated_protocol == NULL) - continue; - if (!vec_is_string(ticket_negotiated_protocol, tls->negotiated_protocol)) - continue; - } - /* check the length of the decrypted psk and the PSK binder */ - if (ticket_psk.len != tls->key_schedule->hashes[0].algo->digest_size) - continue; - if (ch->psk.identities.list[*psk_index].binder.len != tls->key_schedule->hashes[0].algo->digest_size) - continue; - - /* found */ - goto Found; - } - - /* not found */ - *psk_index = SIZE_MAX; - *accept_early_data = 0; - tls->key_share = NULL; - ret = 0; - goto Exit; - -Found: - if ((ret = key_schedule_extract(tls->key_schedule, ticket_psk)) != 0) - goto Exit; - if ((ret = derive_secret(tls->key_schedule, binder_key, "res binder")) != 0) - goto Exit; - key_schedule_update_hash(tls->key_schedule, ch_trunc.base, ch_trunc.len); - if ((ret = calc_verify_data(verify_data, tls->key_schedule, binder_key)) != 0) - goto Exit; - if (memcmp(ch->psk.identities.list[*psk_index].binder.base, verify_data, tls->key_schedule->hashes[0].algo->digest_size) != 0) { - ret = PTLS_ALERT_DECRYPT_ERROR; - goto Exit; - } - ret = 0; - -Exit: - ptls_buffer_dispose(&decbuf); - ptls_clear_memory(binder_key, sizeof(binder_key)); - ptls_clear_memory(verify_data, sizeof(verify_data)); - return ret; -} - -static int calc_cookie_signature(ptls_t *tls, ptls_handshake_properties_t *properties, - ptls_key_exchange_algorithm_t *negotiated_group, ptls_iovec_t tbs, uint8_t *sig) -{ - ptls_hash_algorithm_t *algo = tls->ctx->cipher_suites[0]->hash; - ptls_hash_context_t *hctx; - - if ((hctx = ptls_hmac_create(algo, properties->server.cookie.key, algo->digest_size)) == NULL) - return PTLS_ERROR_NO_MEMORY; - -#define UPDATE_BLOCK(p, _len) \ - do { \ - size_t len = (_len); \ - assert(len < UINT8_MAX); \ - uint8_t len8 = (uint8_t)len; \ - hctx->update(hctx, &len8, 1); \ - hctx->update(hctx, (p), len); \ - } while (0) -#define UPDATE16(_v) \ - do { \ - uint16_t v = (_v); \ - uint8_t b[2] = {v >> 8, v & 0xff}; \ - hctx->update(hctx, b, 2); \ - } while (0) - - UPDATE_BLOCK(tls->client_random, sizeof(tls->client_random)); - UPDATE_BLOCK(tls->server_name, tls->server_name != NULL ? strlen(tls->server_name) : 0); - UPDATE16(tls->cipher_suite->id); - UPDATE16(negotiated_group->id); - UPDATE_BLOCK(properties->server.cookie.additional_data.base, properties->server.cookie.additional_data.len); - - UPDATE_BLOCK(tbs.base, tbs.len); - -#undef UPDATE_BLOCK -#undef UPDATE16 - - hctx->final(hctx, sig, PTLS_HASH_FINAL_MODE_FREE); - return 0; -} - -static int server_handle_hello(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t message, ptls_handshake_properties_t *properties) -{ -#define EMIT_SERVER_HELLO(sched, fill_rand, extensions) \ - buffer_push_handshake(sendbuf, (sched), NULL, PTLS_HANDSHAKE_TYPE_SERVER_HELLO, { \ - ptls_buffer_push16(sendbuf, 0x0303 /* legacy version */); \ - if ((ret = ptls_buffer_reserve(sendbuf, PTLS_HELLO_RANDOM_SIZE)) != 0) \ - goto Exit; \ - do { \ - fill_rand \ - } while (0); \ - sendbuf->off += PTLS_HELLO_RANDOM_SIZE; \ - ptls_buffer_push_block(sendbuf, 1, { ptls_buffer_pushv(sendbuf, ch.legacy_session_id.base, ch.legacy_session_id.len); }); \ - ptls_buffer_push16(sendbuf, tls->cipher_suite->id); \ - ptls_buffer_push(sendbuf, 0); \ - ptls_buffer_push_block(sendbuf, 2, { \ - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS, \ - { ptls_buffer_push16(sendbuf, ch.selected_version); }); \ - do { \ - extensions \ - } while (0); \ - }); \ - }); - -#define EMIT_HELLO_RETRY_REQUEST(sched, negotiated_group, additional_extensions) \ - EMIT_SERVER_HELLO((sched), { memcpy(sendbuf->base + sendbuf->off, hello_retry_random, PTLS_HELLO_RANDOM_SIZE); }, \ - { \ - ptls_key_exchange_algorithm_t *_negotiated_group = (negotiated_group); \ - if (_negotiated_group != NULL) { \ - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE, \ - { ptls_buffer_push16(sendbuf, _negotiated_group->id); }); \ - } \ - do { \ - additional_extensions \ - } while (0); \ - }); - - struct st_ptls_client_hello_t ch = {NULL, {NULL}, {NULL}, 0, {NULL}, {NULL}, {NULL}, - {{0}}, {NULL}, {{{NULL}}}, {{NULL}}, {NULL}, {{UINT16_MAX}}}; - struct { - ptls_key_exchange_algorithm_t *algorithm; - ptls_iovec_t peer_key; - } key_share = {NULL}; - enum { HANDSHAKE_MODE_FULL, HANDSHAKE_MODE_PSK, HANDSHAKE_MODE_PSK_DHE } mode; - size_t psk_index = SIZE_MAX; - ptls_iovec_t pubkey = {0}, ecdh_secret = {0}; - uint8_t finished_key[PTLS_MAX_DIGEST_SIZE]; - int accept_early_data = 0, is_second_flight = tls->state == PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO, ret; - - /* decode ClientHello */ - if ((ret = decode_client_hello(tls, &ch, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len, properties)) != - 0) - goto Exit; - if (tls->ctx->require_dhe_on_psk) - ch.psk.ke_modes &= ~(1u << PTLS_PSK_KE_MODE_PSK); - - /* handle client_random and SNI */ - if (!is_second_flight) { - memcpy(tls->client_random, ch.random_bytes, sizeof(tls->client_random)); - if (ch.server_name.base != NULL) { - if ((tls->server_name = malloc(ch.server_name.len + 1)) == NULL) { - ret = PTLS_ERROR_NO_MEMORY; - goto Exit; - } - memcpy(tls->server_name, ch.server_name.base, ch.server_name.len); - tls->server_name[ch.server_name.len] = '\0'; - } - if (tls->ctx->on_client_hello != NULL && - (ret = tls->ctx->on_client_hello->cb(tls->ctx->on_client_hello, tls, ch.server_name, ch.alpn.list, ch.alpn.count, - ch.signature_algorithms.list, ch.signature_algorithms.count)) != 0) - goto Exit; - } else { - if (ch.psk.early_data_indication) { - ret = PTLS_ALERT_DECODE_ERROR; - goto Exit; - } - if (memcmp(tls->client_random, ch.random_bytes, sizeof(tls->client_random)) != 0 || - (tls->server_name != NULL) != (ch.server_name.base != NULL) || - (tls->server_name != NULL && - !(strncmp(tls->server_name, (char *)ch.server_name.base, ch.server_name.len) == 0 && - tls->server_name[ch.server_name.len] == '\0'))) { - ret = PTLS_ALERT_HANDSHAKE_FAILURE; - goto Exit; - } - } - - { /* select (or check) cipher-suite, create key_schedule */ - ptls_cipher_suite_t *cs; - if ((ret = select_cipher_suite(&cs, tls->ctx->cipher_suites, ch.cipher_suites.base, - ch.cipher_suites.base + ch.cipher_suites.len)) != 0) - goto Exit; - if (!is_second_flight) { - tls->cipher_suite = cs; - tls->key_schedule = key_schedule_new(cs, NULL); - } else { - if (tls->cipher_suite != cs) { - ret = PTLS_ALERT_HANDSHAKE_FAILURE; - goto Exit; - } - } - } - - /* select key_share */ - if (ch.key_shares.base != NULL && - (ret = select_key_share(&key_share.algorithm, &key_share.peer_key, tls->ctx->key_exchanges, ch.key_shares.base, - ch.key_shares.base + ch.key_shares.len)) != 0) - goto Exit; - - if (!is_second_flight) { - if (ch.cookie.all.len != 0 && key_share.algorithm != NULL) { - - /* use cookie to check the integrity of the handshake, and update the context */ - uint8_t sig[PTLS_MAX_DIGEST_SIZE]; - size_t sigsize = tls->ctx->cipher_suites[0]->hash->digest_size; - if ((ret = calc_cookie_signature(tls, properties, key_share.algorithm, ch.cookie.tbs, sig)) != 0) - goto Exit; - if (!(ch.cookie.signature.len == sigsize && memcmp(ch.cookie.signature.base, sig, sigsize) == 0)) { - ret = PTLS_ALERT_HANDSHAKE_FAILURE; - goto Exit; - } - /* integrity check passed; update states */ - key_schedule_update_ch1hash_prefix(tls->key_schedule); - key_schedule_update_hash(tls->key_schedule, ch.cookie.ch1_hash.base, ch.cookie.ch1_hash.len); - 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_HELLO_RETRY_REQUEST(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); }); - }); - sendbuf->off = hrr_start; - is_second_flight = 1; - - } else if (key_share.algorithm == NULL || (properties != NULL && properties->server.enforce_retry)) { - - /* send HelloRetryRequest */ - if (ch.negotiated_groups.base == NULL) { - ret = PTLS_ALERT_MISSING_EXTENSION; - goto Exit; - } - ptls_key_exchange_algorithm_t *negotiated_group; - if ((ret = select_negotiated_group(&negotiated_group, tls->ctx->key_exchanges, ch.negotiated_groups.base, - ch.negotiated_groups.base + ch.negotiated_groups.len)) != 0) - goto Exit; - key_schedule_update_hash(tls->key_schedule, message.base, message.len); - assert(tls->key_schedule->generation == 0); - if (properties != NULL && properties->server.retry_uses_cookie) { - /* emit HRR with cookie (note: we MUST omit KeyShare if the client has specified the correct one; see 46554f0) */ - EMIT_HELLO_RETRY_REQUEST(NULL, key_share.algorithm != NULL ? NULL : negotiated_group, { - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_COOKIE, { - ptls_buffer_push_block(sendbuf, 2, { - /* push to-be-signed data */ - size_t tbs_start = sendbuf->off; - ptls_buffer_push_block(sendbuf, 2, { - /* first block of the cookie data is the hash(ch1) */ - ptls_buffer_push_block(sendbuf, 1, { - size_t sz = tls->cipher_suite->hash->digest_size; - if ((ret = ptls_buffer_reserve(sendbuf, sz)) != 0) - goto Exit; - 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; - /* push the signature */ - ptls_buffer_push_block(sendbuf, 1, { - size_t sz = tls->ctx->cipher_suites[0]->hash->digest_size; - if ((ret = ptls_buffer_reserve(sendbuf, sz)) != 0) - goto Exit; - if ((ret = calc_cookie_signature(tls, properties, negotiated_group, - ptls_iovec_init(sendbuf->base + tbs_start, tbs_len), - sendbuf->base + sendbuf->off)) != 0) - goto Exit; - sendbuf->off += sz; - }); - }); - }); - }); - if ((ret = push_change_cipher_spec(tls, sendbuf)) != 0) - goto Exit; - ret = PTLS_ERROR_STATELESS_RETRY; - } else { - /* invoking stateful retry; roll the key schedule and emit HRR */ - key_schedule_transform_post_ch1hash(tls->key_schedule); - key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0)); - EMIT_HELLO_RETRY_REQUEST(tls->key_schedule, key_share.algorithm != NULL ? NULL : negotiated_group, {}); - if ((ret = push_change_cipher_spec(tls, sendbuf)) != 0) - goto Exit; - tls->state = PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO; - if (ch.psk.early_data_indication) - tls->skip_early_data = 1; - ret = PTLS_ERROR_IN_PROGRESS; - } - goto Exit; - } - } - - /* handle unknown extensions */ - if ((ret = report_unknown_extensions(tls, properties, ch.unknown_extensions)) != 0) - goto Exit; - - /* try psk handshake */ - if (!is_second_flight && ch.psk.hash_end != 0 && - (ch.psk.ke_modes & ((1u << PTLS_PSK_KE_MODE_PSK) | (1u << PTLS_PSK_KE_MODE_PSK_DHE))) != 0 && - tls->ctx->encrypt_ticket != NULL) { - if ((ret = try_psk_handshake(tls, &psk_index, &accept_early_data, &ch, - ptls_iovec_init(message.base, ch.psk.hash_end - message.base))) != 0) - goto Exit; - } - - /* adjust key_schedule, determine handshake mode */ - if (psk_index == SIZE_MAX) { - key_schedule_update_hash(tls->key_schedule, message.base, message.len); - if (!is_second_flight) { - assert(tls->key_schedule->generation == 0); - key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0)); - } - mode = HANDSHAKE_MODE_FULL; - if (properties != NULL) - properties->server.selected_psk_binder.len = 0; - } else { - key_schedule_update_hash(tls->key_schedule, ch.psk.hash_end, message.base + message.len - ch.psk.hash_end); - if ((ch.psk.ke_modes & (1u << PTLS_PSK_KE_MODE_PSK)) != 0) { - mode = HANDSHAKE_MODE_PSK; - } else { - assert((ch.psk.ke_modes & (1u << PTLS_PSK_KE_MODE_PSK_DHE)) != 0); - mode = HANDSHAKE_MODE_PSK_DHE; - } - tls->is_psk_handshake = 1; - if (properties != NULL) { - ptls_iovec_t *selected = &ch.psk.identities.list[psk_index].binder; - memcpy(properties->server.selected_psk_binder.base, selected->base, selected->len); - properties->server.selected_psk_binder.len = selected->len; - } - if ((ret = derive_exporter_secret(tls, 1)) != 0) - goto Exit; - } - - if (accept_early_data && tls->ctx->max_early_data_size != 0 && psk_index == 0) { - if ((tls->early_data = malloc(sizeof(*tls->early_data))) == NULL) { - ret = PTLS_ERROR_NO_MEMORY; - goto Exit; - } - if ((ret = setup_traffic_protection(tls, 0, "c e traffic", "CLIENT_EARLY_TRAFFIC_SECRET")) != 0) - goto Exit; - } - - /* run key-exchange, to obtain pubkey and secret */ - if (mode != HANDSHAKE_MODE_PSK) { - if (key_share.algorithm == NULL) { - ret = ch.key_shares.base != NULL ? PTLS_ALERT_HANDSHAKE_FAILURE : PTLS_ALERT_MISSING_EXTENSION; - goto Exit; - } - if ((ret = key_share.algorithm->exchange(&pubkey, &ecdh_secret, key_share.peer_key)) != 0) - goto Exit; - tls->key_share = key_share.algorithm; - } - - /* send ServerHello */ - EMIT_SERVER_HELLO(tls->key_schedule, { tls->ctx->random_bytes(sendbuf->base + sendbuf->off, PTLS_HELLO_RANDOM_SIZE); }, - { - if (mode != HANDSHAKE_MODE_PSK) { - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE, { - ptls_buffer_push16(sendbuf, key_share.algorithm->id); - ptls_buffer_push_block(sendbuf, 2, { ptls_buffer_pushv(sendbuf, pubkey.base, pubkey.len); }); - }); - } - if (mode != HANDSHAKE_MODE_FULL) { - buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_PRE_SHARED_KEY, - { ptls_buffer_push16(sendbuf, (uint16_t)psk_index); }); - } - }); - if ((ret = push_change_cipher_spec(tls, sendbuf)) != 0) - goto Exit; - - /* create protection contexts for the handshake */ - assert(tls->key_schedule->generation == 1); - key_schedule_extract(tls->key_schedule, ecdh_secret); - if ((ret = setup_traffic_protection(tls, 1, "s hs traffic", "SERVER_HANDSHAKE_TRAFFIC_SECRET")) != 0) - goto Exit; - if (tls->early_data != NULL) { - if ((ret = derive_secret(tls->key_schedule, tls->early_data->next_secret, "c hs traffic")) != 0) - goto Exit; - } else { - if ((ret = setup_traffic_protection(tls, 0, "c hs traffic", "CLIENT_HANDSHAKE_TRAFFIC_SECRET")) != 0) - goto Exit; - if (ch.psk.early_data_indication) - tls->skip_early_data = 1; - } - - /* send EncryptedExtensions */ - 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 (mode == HANDSHAKE_MODE_FULL) { - if (ch.signature_algorithms.count == 0) { - ret = PTLS_ALERT_MISSING_EXTENSION; - goto Exit; - } - /* send Certificate */ - 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; - } - }); - } - }); - }); - /* build and send CertificateVerify */ - 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; - }); - }); - } - - send_finished(tls, sendbuf); - - assert(tls->key_schedule->generation == 2); - if ((ret = key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0))) != 0) - goto Exit; - if ((ret = setup_traffic_protection(tls, 1, "s ap traffic", "SERVER_TRAFFIC_SECRET_0")) != 0) - goto Exit; - if ((ret = derive_secret(tls->key_schedule, tls->server.pending_traffic_secret, "c ap traffic")) != 0) - goto Exit; - if ((ret = derive_exporter_secret(tls, 0)) != 0) - goto Exit; - - tls->state = tls->early_data != NULL ? PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA : PTLS_STATE_SERVER_EXPECT_FINISHED; - - /* send session ticket if necessary */ - if (ch.psk.ke_modes != 0 && tls->ctx->ticket_lifetime != 0) { - if ((ret = send_session_ticket(tls, sendbuf)) != 0) - goto Exit; - } - - ret = 0; - -Exit: - free(pubkey.base); - free(ecdh_secret.base); - ptls_clear_memory(finished_key, sizeof(finished_key)); - return ret; - -#undef EMIT_SERVER_HELLO -#undef EMIT_HELLO_RETRY_REQUEST -} - -static int server_handle_end_of_early_data(ptls_t *tls, ptls_iovec_t message) -{ - int ret; - - if ((ret = retire_early_data_secret(tls, 0)) != 0) - goto Exit; - - key_schedule_update_hash(tls->key_schedule, message.base, message.len); - tls->state = PTLS_STATE_SERVER_EXPECT_FINISHED; - ret = PTLS_ERROR_IN_PROGRESS; - -Exit: - return ret; -} - -static int server_handle_finished(ptls_t *tls, ptls_iovec_t message) -{ - int ret; - - if ((ret = verify_finished(tls, message)) != 0) - return ret; - - memcpy(tls->traffic_protection.dec.secret, tls->server.pending_traffic_secret, sizeof(tls->server.pending_traffic_secret)); - ptls_clear_memory(tls->server.pending_traffic_secret, sizeof(tls->server.pending_traffic_secret)); - if ((ret = setup_traffic_protection(tls, 0, NULL, "CLIENT_TRAFFIC_SECRET_0")) != 0) - return ret; - - key_schedule_update_hash(tls->key_schedule, message.base, message.len); - - tls->state = PTLS_STATE_SERVER_POST_HANDSHAKE; - return 0; -} - -static int parse_record_header(struct st_ptls_record_t *rec, const uint8_t *src) -{ - rec->type = src[0]; - rec->version = ntoh16(src + 1); - rec->length = ntoh16(src + 3); - - if (rec->length > - (size_t)(rec->type == PTLS_CONTENT_TYPE_APPDATA ? PTLS_MAX_ENCRYPTED_RECORD_SIZE : PTLS_MAX_PLAINTEXT_RECORD_SIZE)) - return PTLS_ALERT_DECODE_ERROR; - - return 0; -} - -static int parse_record(ptls_t *tls, struct st_ptls_record_t *rec, const uint8_t *src, size_t *len) -{ - int ret; - - if (tls->recvbuf.rec.base == NULL && *len >= 5) { - /* fast path */ - if ((ret = parse_record_header(rec, src)) != 0) - return ret; - if (5 + rec->length <= *len) { - rec->fragment = src + 5; - *len = rec->length + 5; - return 0; - } - } - - /* slow path */ - const uint8_t *const end = src + *len; - *rec = (struct st_ptls_record_t){0}; - - if (tls->recvbuf.rec.base == NULL) { - ptls_buffer_init(&tls->recvbuf.rec, "", 0); - if ((ret = ptls_buffer_reserve(&tls->recvbuf.rec, 5)) != 0) - return ret; - } - - /* fill and parse the header */ - while (tls->recvbuf.rec.off < 5) { - if (src == end) - return PTLS_ERROR_IN_PROGRESS; - tls->recvbuf.rec.base[tls->recvbuf.rec.off++] = *src++; - } - if ((ret = parse_record_header(rec, tls->recvbuf.rec.base)) != 0) - return ret; - - /* fill the fragment */ - size_t addlen = rec->length + 5 - tls->recvbuf.rec.off; - if (addlen != 0) { - if ((ret = ptls_buffer_reserve(&tls->recvbuf.rec, addlen)) != 0) - return ret; - if (addlen > (size_t)(end - src)) - addlen = end - src; - if (addlen != 0) { - memcpy(tls->recvbuf.rec.base + tls->recvbuf.rec.off, src, addlen); - tls->recvbuf.rec.off += addlen; - src += addlen; - } - } - - /* set rec->fragment if a complete record has been parsed */ - if (tls->recvbuf.rec.off == rec->length + 5) { - rec->fragment = tls->recvbuf.rec.base + 5; - ret = 0; - } else { - ret = PTLS_ERROR_IN_PROGRESS; - } - - *len -= end - src; - return ret; -} - -static void update_open_count(ptls_context_t *ctx, ssize_t delta) -{ - if (ctx->update_open_count != NULL) - ctx->update_open_count->cb(ctx->update_open_count, delta); -} - -ptls_t *ptls_new(ptls_context_t *ctx, int is_server) -{ - ptls_t *tls; - - assert(ctx->get_time != NULL && "please set ctx->get_time to `&ptls_get_time`; see #92"); - - if ((tls = malloc(sizeof(*tls))) == NULL) - return NULL; - - update_open_count(ctx, 1); - *tls = (ptls_t){ctx}; - tls->is_server = is_server; - tls->send_change_cipher_spec = ctx->send_change_cipher_spec; - if (!is_server) { - tls->state = PTLS_STATE_CLIENT_HANDSHAKE_START; - tls->ctx->random_bytes(tls->client_random, sizeof(tls->client_random)); - tls->ctx->random_bytes(tls->client.legacy_session_id, sizeof(tls->client.legacy_session_id)); - } else { - tls->state = PTLS_STATE_SERVER_EXPECT_CLIENT_HELLO; - } - - return tls; -} - -void ptls_free(ptls_t *tls) -{ - ptls_buffer_dispose(&tls->recvbuf.rec); - ptls_buffer_dispose(&tls->recvbuf.mess); - free_exporter_master_secret(tls, 1); - free_exporter_master_secret(tls, 0); - if (tls->key_schedule != NULL) - key_schedule_free(tls->key_schedule); - if (tls->traffic_protection.dec.aead != NULL) - ptls_aead_free(tls->traffic_protection.dec.aead); - if (tls->traffic_protection.enc.aead != NULL) - ptls_aead_free(tls->traffic_protection.enc.aead); - free(tls->server_name); - free(tls->negotiated_protocol); - if (tls->is_server) { - /* nothing to do */ - } else { - if (tls->client.key_share_ctx != NULL) - tls->client.key_share_ctx->on_exchange(&tls->client.key_share_ctx, NULL, ptls_iovec_init(NULL, 0)); - if (tls->client.certificate_verify.cb != NULL) - tls->client.certificate_verify.cb(tls->client.certificate_verify.verify_ctx, ptls_iovec_init(NULL, 0), - ptls_iovec_init(NULL, 0)); - } - if (tls->early_data != NULL) { - ptls_clear_memory(tls->early_data, sizeof(*tls->early_data)); - free(tls->early_data); - } - update_open_count(tls->ctx, -1); - ptls_clear_memory(tls, sizeof(*tls)); - free(tls); -} - -ptls_context_t *ptls_get_context(ptls_t *tls) -{ - return tls->ctx; -} - -void ptls_set_context(ptls_t *tls, ptls_context_t *ctx) -{ - update_open_count(ctx, 1); - update_open_count(tls->ctx, -1); - tls->ctx = ctx; -} - -ptls_iovec_t ptls_get_client_random(ptls_t *tls) -{ - return ptls_iovec_init(tls->client_random, PTLS_HELLO_RANDOM_SIZE); -} - -ptls_cipher_suite_t *ptls_get_cipher(ptls_t *tls) -{ - return tls->cipher_suite; -} - -const char *ptls_get_server_name(ptls_t *tls) -{ - return tls->server_name; -} - -int ptls_set_server_name(ptls_t *tls, const char *server_name, size_t server_name_len) -{ - char *duped = NULL; - - if (server_name != NULL) { - if (server_name_len == 0) - server_name_len = strlen(server_name); - if ((duped = malloc(server_name_len + 1)) == NULL) - return PTLS_ERROR_NO_MEMORY; - memcpy(duped, server_name, server_name_len); - duped[server_name_len] = '\0'; - } - - free(tls->server_name); - tls->server_name = duped; - - return 0; -} - -const char *ptls_get_negotiated_protocol(ptls_t *tls) -{ - return tls->negotiated_protocol; -} - -int ptls_set_negotiated_protocol(ptls_t *tls, const char *protocol, size_t protocol_len) -{ - char *duped = NULL; - - if (protocol != NULL) { - if (protocol_len == 0) - protocol_len = strlen(protocol); - if ((duped = malloc(protocol_len + 1)) == NULL) - return PTLS_ERROR_NO_MEMORY; - memcpy(duped, protocol, protocol_len); - duped[protocol_len] = '\0'; - } - - free(tls->negotiated_protocol); - tls->negotiated_protocol = duped; - - return 0; -} - -int ptls_handshake_is_complete(ptls_t *tls) -{ - return tls->state >= PTLS_STATE_POST_HANDSHAKE_MIN; -} - -int ptls_is_psk_handshake(ptls_t *tls) -{ - return tls->is_psk_handshake; -} - -void **ptls_get_data_ptr(ptls_t *tls) -{ - return &tls->data_ptr; -} - -static int handle_handshake_message(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t message, int is_end_of_record, - ptls_handshake_properties_t *properties) -{ - uint8_t type = message.base[0]; - int ret; - - switch (tls->state) { - case PTLS_STATE_CLIENT_EXPECT_SERVER_HELLO: - case PTLS_STATE_CLIENT_EXPECT_SECOND_SERVER_HELLO: - if (type == PTLS_HANDSHAKE_TYPE_SERVER_HELLO && is_end_of_record) { - ret = client_handle_hello(tls, sendbuf, message, properties); - } else { - ret = PTLS_ALERT_UNEXPECTED_MESSAGE; - } - break; - case PTLS_STATE_CLIENT_EXPECT_ENCRYPTED_EXTENSIONS: - if (type == PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS) { - ret = client_handle_encrypted_extensions(tls, message, properties); - } else { - ret = PTLS_ALERT_UNEXPECTED_MESSAGE; - } - break; - case PTLS_STATE_CLIENT_EXPECT_CERTIFICATE: - if (type == PTLS_HANDSHAKE_TYPE_CERTIFICATE) { - ret = client_handle_certificate(tls, message); - } else { - ret = PTLS_ALERT_UNEXPECTED_MESSAGE; - } - break; - case PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_VERIFY: - if (type == PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) { - ret = client_handle_certificate_verify(tls, message); - } else { - ret = PTLS_ALERT_UNEXPECTED_MESSAGE; - } - break; - case PTLS_STATE_CLIENT_EXPECT_FINISHED: - if (type == PTLS_HANDSHAKE_TYPE_FINISHED && is_end_of_record) { - ret = client_handle_finished(tls, sendbuf, message); - } else { - ret = PTLS_ALERT_UNEXPECTED_MESSAGE; - } - break; - case PTLS_STATE_SERVER_EXPECT_CLIENT_HELLO: - case PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO: - if (type == PTLS_HANDSHAKE_TYPE_CLIENT_HELLO && is_end_of_record) { - ret = server_handle_hello(tls, sendbuf, message, properties); - } else { - ret = PTLS_ALERT_HANDSHAKE_FAILURE; - } - break; - case PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA: - if (type == PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA) { - ret = server_handle_end_of_early_data(tls, message); - } else { - ret = PTLS_ALERT_UNEXPECTED_MESSAGE; - } - break; - case PTLS_STATE_SERVER_EXPECT_FINISHED: - if (type == PTLS_HANDSHAKE_TYPE_FINISHED && is_end_of_record) { - ret = server_handle_finished(tls, message); - } else { - ret = PTLS_ALERT_HANDSHAKE_FAILURE; - } - break; - case PTLS_STATE_CLIENT_POST_HANDSHAKE: - switch (type) { - case PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET: - ret = client_handle_new_session_ticket(tls, message); - break; - default: - ret = PTLS_ALERT_UNEXPECTED_MESSAGE; - break; - } - break; - case PTLS_STATE_SERVER_POST_HANDSHAKE: - ret = PTLS_ALERT_UNEXPECTED_MESSAGE; - break; - default: - assert(!"unexpected state"); - break; - } - - return ret; -} - -static int handle_alert(ptls_t *tls, const uint8_t *src, size_t len) -{ - if (len != 2) - return PTLS_ALERT_DECODE_ERROR; - - uint8_t level = src[0], desc = src[1]; - - /* ignore certain warnings */ - if (level == PTLS_ALERT_LEVEL_WARNING) { - switch (desc) { - case PTLS_ALERT_USER_CANCELED: - return 0; - default: - break; - } - } - - /* all other alerts are considered fatal, regardless of the transmitted level (section 6) */ - return PTLS_ALERT_TO_PEER_ERROR(desc); -} - -static int handle_handshake_record(ptls_t *tls, int (*cb)(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_iovec_t message, - int is_end_of_record, ptls_handshake_properties_t *properties), - ptls_buffer_t *sendbuf, struct st_ptls_record_t *rec, ptls_handshake_properties_t *properties) -{ - int ret; - - /* handshake */ - if (rec->type != PTLS_CONTENT_TYPE_HANDSHAKE) - return PTLS_ALERT_DECODE_ERROR; - - /* flatten the unhandled messages */ - const uint8_t *src, *src_end; - if (tls->recvbuf.mess.base == NULL) { - src = rec->fragment; - src_end = src + rec->length; - } else { - if ((ret = ptls_buffer_reserve(&tls->recvbuf.mess, rec->length)) != 0) - return ret; - memcpy(tls->recvbuf.mess.base + tls->recvbuf.mess.off, rec->fragment, rec->length); - tls->recvbuf.mess.off += rec->length; - src = tls->recvbuf.mess.base; - src_end = src + tls->recvbuf.mess.off; - } - - /* handle the messages */ - ret = PTLS_ERROR_IN_PROGRESS; - while (src_end - src >= 4) { - size_t mess_len = 4 + ntoh24(src + 1); - if (src_end - src < (int)mess_len) - break; - ret = cb(tls, sendbuf, ptls_iovec_init(src, mess_len), src_end - src == mess_len, properties); - switch (ret) { - case 0: - case PTLS_ERROR_IN_PROGRESS: - break; - default: - ptls_buffer_dispose(&tls->recvbuf.mess); - return ret; - } - src += mess_len; - } - - /* keep last partial message in buffer */ - if (src != src_end) { - if (tls->recvbuf.mess.base == NULL) { - ptls_buffer_init(&tls->recvbuf.mess, "", 0); - if ((ret = ptls_buffer_reserve(&tls->recvbuf.mess, src_end - src)) != 0) - return ret; - memcpy(tls->recvbuf.mess.base, src, src_end - src); - } else { - 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); - } - - return ret; -} - -static int handle_input(ptls_t *tls, ptls_buffer_t *sendbuf, ptls_buffer_t *decryptbuf, const void *input, size_t *inlen, - ptls_handshake_properties_t *properties) -{ - struct st_ptls_record_t rec; - int ret; - - /* extract the record */ - if ((ret = parse_record(tls, &rec, input, inlen)) != 0) - return ret; - assert(rec.fragment != NULL); - - /* decrypt the record */ - if (rec.type == PTLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { - if (tls->state < PTLS_STATE_POST_HANDSHAKE_MIN) { - if (!(rec.length == 1 && rec.fragment[0] == 0x01)) - return PTLS_ALERT_ILLEGAL_PARAMETER; - } else { - return PTLS_ALERT_HANDSHAKE_FAILURE; - } - ret = PTLS_ERROR_IN_PROGRESS; - goto NextRecord; - } - if (tls->traffic_protection.dec.aead != NULL && rec.type != PTLS_CONTENT_TYPE_ALERT) { - if (rec.type != PTLS_CONTENT_TYPE_APPDATA) - return PTLS_ALERT_HANDSHAKE_FAILURE; - if ((ret = ptls_buffer_reserve(decryptbuf, 5 + rec.length)) != 0) - return ret; - if ((ret = aead_decrypt(&tls->traffic_protection.dec, decryptbuf->base + decryptbuf->off, &rec.length, rec.fragment, - rec.length)) != 0) { - if (tls->skip_early_data) { - ret = PTLS_ERROR_IN_PROGRESS; - goto NextRecord; - } - return ret; - } - rec.fragment = decryptbuf->base + decryptbuf->off; - /* skip padding */ - for (; rec.length != 0; --rec.length) - if (rec.fragment[rec.length - 1] != 0) - break; - if (rec.length == 0) - return PTLS_ALERT_UNEXPECTED_MESSAGE; - rec.type = rec.fragment[--rec.length]; - } else if (rec.type == PTLS_CONTENT_TYPE_APPDATA && tls->skip_early_data) { - ret = PTLS_ERROR_IN_PROGRESS; - goto NextRecord; - } - - if (tls->recvbuf.mess.base != NULL || rec.type == PTLS_CONTENT_TYPE_HANDSHAKE) { - /* handshake record */ - ret = handle_handshake_record(tls, handle_handshake_message, sendbuf, &rec, properties); - } else { - /* handling of an alert or an application record */ - switch (rec.type) { - case PTLS_CONTENT_TYPE_APPDATA: - if (tls->state >= PTLS_STATE_POST_HANDSHAKE_MIN) { - decryptbuf->off += rec.length; - ret = 0; - } else if (tls->state == PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA) { - if (tls->traffic_protection.dec.aead != NULL) - decryptbuf->off += rec.length; - ret = 0; - } else { - ret = PTLS_ALERT_UNEXPECTED_MESSAGE; - } - break; - case PTLS_CONTENT_TYPE_ALERT: - ret = handle_alert(tls, rec.fragment, rec.length); - break; - default: - ret = PTLS_ALERT_UNEXPECTED_MESSAGE; - break; - } - } - -NextRecord: - ptls_buffer_dispose(&tls->recvbuf.rec); - return ret; -} - -int ptls_handshake(ptls_t *tls, ptls_buffer_t *sendbuf, const void *input, size_t *inlen, ptls_handshake_properties_t *properties) -{ - size_t sendbuf_orig_off = sendbuf->off; - int ret; - - assert(tls->state < PTLS_STATE_POST_HANDSHAKE_MIN); - - /* special handlings */ - switch (tls->state) { - case PTLS_STATE_CLIENT_HANDSHAKE_START: - assert(input == NULL || *inlen == 0); - assert(tls->ctx->key_exchanges[0] != NULL); - return send_client_hello(tls, sendbuf, properties, NULL); - default: - break; - } - - const uint8_t *src = input, *const src_end = src + *inlen; - ptls_buffer_t decryptbuf; - uint8_t decryptbuf_small[256]; - - ptls_buffer_init(&decryptbuf, decryptbuf_small, sizeof(decryptbuf_small)); - - /* perform handhake until completion or until all the input has been swallowed */ - ret = PTLS_ERROR_IN_PROGRESS; - while (ret == PTLS_ERROR_IN_PROGRESS && src != src_end) { - size_t consumed = src_end - src; - ret = handle_input(tls, sendbuf, &decryptbuf, src, &consumed, properties); - src += consumed; - assert(decryptbuf.off == 0); - } - - ptls_buffer_dispose(&decryptbuf); - - switch (ret) { - case 0: - case PTLS_ERROR_IN_PROGRESS: - case PTLS_ERROR_STATELESS_RETRY: - break; - default: - /* flush partially written response */ - ptls_clear_memory(sendbuf->base + sendbuf_orig_off, sendbuf->off - sendbuf_orig_off); - sendbuf->off = sendbuf_orig_off; - /* send alert immediately */ - if (PTLS_ERROR_GET_CLASS(ret) != PTLS_ERROR_CLASS_PEER_ALERT) - if (ptls_send_alert(tls, sendbuf, PTLS_ALERT_LEVEL_FATAL, - PTLS_ERROR_GET_CLASS(ret) == PTLS_ERROR_CLASS_SELF_ALERT ? ret : PTLS_ALERT_INTERNAL_ERROR) != 0) - sendbuf->off = sendbuf_orig_off; - break; - } - - *inlen -= src_end - src; - return ret; -} - -int ptls_receive(ptls_t *tls, ptls_buffer_t *decryptbuf, const void *_input, size_t *inlen) -{ - const uint8_t *input = (const uint8_t *)_input, *const end = input + *inlen; - size_t decryptbuf_orig_size = decryptbuf->off; - int ret = 0; - - assert(tls->state >= PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA); - - /* loop until we decrypt some application data (or an error) */ - while (ret == 0 && input != end && decryptbuf_orig_size == decryptbuf->off) { - size_t consumed = end - input; - ret = handle_input(tls, NULL, decryptbuf, input, &consumed, NULL); - input += consumed; - - switch (ret) { - case 0: - break; - case PTLS_ERROR_IN_PROGRESS: - ret = 0; - break; - case PTLS_ERROR_CLASS_PEER_ALERT + PTLS_ALERT_CLOSE_NOTIFY: - /* TODO send close alert */ - break; - default: - if (PTLS_ERROR_GET_CLASS(ret) == PTLS_ERROR_CLASS_SELF_ALERT) { - /* TODO send alert */ - } - break; - } - } - - *inlen -= end - input; - - return ret; -} - -int ptls_send(ptls_t *tls, ptls_buffer_t *sendbuf, const void *input, size_t inlen) -{ - assert(tls->traffic_protection.enc.aead != NULL); - 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) -{ - return 6 + tls->traffic_protection.enc.aead->algo->tag_size; -} - -int ptls_send_alert(ptls_t *tls, ptls_buffer_t *sendbuf, uint8_t level, uint8_t description) -{ - size_t rec_start = sendbuf->off; - int ret = 0; - - buffer_push_record(sendbuf, PTLS_CONTENT_TYPE_ALERT, { ptls_buffer_push(sendbuf, level, description); }); - /* encrypt the alert if we have the encryption keys, unless when it is the early data key */ - if (tls->traffic_protection.enc.aead != NULL && !(tls->state <= PTLS_STATE_CLIENT_EXPECT_FINISHED)) { - if ((ret = buffer_encrypt_record(sendbuf, rec_start, &tls->traffic_protection.enc)) != 0) - goto Exit; - } - -Exit: - return ret; -} - -int ptls_export_secret(ptls_t *tls, void *output, size_t outlen, const char *label, ptls_iovec_t context_value, int is_early) -{ - ptls_hash_algorithm_t *algo = tls->key_schedule->hashes[0].algo; - ptls_hash_context_t *hctx; - uint8_t *master_secret = is_early ? tls->exporter_master_secret.early : tls->exporter_master_secret.one_rtt, - derived_secret[PTLS_MAX_DIGEST_SIZE], context_value_hash[PTLS_MAX_DIGEST_SIZE]; - int ret; - - if (master_secret == NULL) - return PTLS_ERROR_IN_PROGRESS; - - if ((hctx = algo->create()) == NULL) - return PTLS_ERROR_NO_MEMORY; - hctx->update(hctx, context_value.base, context_value.len); - hctx->final(hctx, context_value_hash, PTLS_HASH_FINAL_MODE_FREE); - - if ((ret = ptls_hkdf_expand_label(algo, derived_secret, algo->digest_size, ptls_iovec_init(master_secret, algo->digest_size), - label, ptls_iovec_init(algo->empty_digest, algo->digest_size), NULL)) != 0) - goto Exit; - ret = ptls_hkdf_expand_label(algo, output, outlen, ptls_iovec_init(derived_secret, algo->digest_size), "exporter", - ptls_iovec_init(context_value_hash, algo->digest_size), NULL); - -Exit: - ptls_clear_memory(derived_secret, sizeof(derived_secret)); - ptls_clear_memory(context_value_hash, sizeof(context_value_hash)); - return ret; -} - -struct st_picotls_hmac_context_t { - ptls_hash_context_t super; - ptls_hash_algorithm_t *algo; - ptls_hash_context_t *hash; - uint8_t key[1]; -}; - -static void hmac_update(ptls_hash_context_t *_ctx, const void *src, size_t len) -{ - struct st_picotls_hmac_context_t *ctx = (struct st_picotls_hmac_context_t *)_ctx; - ctx->hash->update(ctx->hash, src, len); -} - -static void hmac_apply_key(struct st_picotls_hmac_context_t *ctx, uint8_t pad) -{ - size_t i; - - for (i = 0; i != ctx->algo->block_size; ++i) - ctx->key[i] ^= pad; - ctx->hash->update(ctx->hash, ctx->key, ctx->algo->block_size); - for (i = 0; i != ctx->algo->block_size; ++i) - ctx->key[i] ^= pad; -} - -static void hmac_final(ptls_hash_context_t *_ctx, void *md, ptls_hash_final_mode_t mode) -{ - struct st_picotls_hmac_context_t *ctx = (struct st_picotls_hmac_context_t *)_ctx; - - assert(mode != PTLS_HASH_FINAL_MODE_SNAPSHOT || !"not supported"); - - if (md != NULL) { - ctx->hash->final(ctx->hash, md, PTLS_HASH_FINAL_MODE_RESET); - hmac_apply_key(ctx, 0x5c); - ctx->hash->update(ctx->hash, md, ctx->algo->digest_size); - } - ctx->hash->final(ctx->hash, md, mode); - - switch (mode) { - case PTLS_HASH_FINAL_MODE_FREE: - ptls_clear_memory(ctx->key, ctx->algo->block_size); - free(ctx); - break; - case PTLS_HASH_FINAL_MODE_RESET: - hmac_apply_key(ctx, 0x36); - break; - default: - assert(!"FIXME"); - break; - } -} - -ptls_hash_context_t *ptls_hmac_create(ptls_hash_algorithm_t *algo, const void *key, size_t key_size) -{ - struct st_picotls_hmac_context_t *ctx; - - if ((ctx = malloc(offsetof(struct st_picotls_hmac_context_t, key) + algo->block_size)) == NULL) - return NULL; - - *ctx = (struct st_picotls_hmac_context_t){{hmac_update, hmac_final}, algo}; - if ((ctx->hash = algo->create()) == NULL) { - free(ctx); - return NULL; - } - memset(ctx->key, 0, algo->block_size); - memcpy(ctx->key, key, key_size); - - hmac_apply_key(ctx, 0x36); - - return &ctx->super; -} - -int ptls_hkdf_extract(ptls_hash_algorithm_t *algo, void *output, ptls_iovec_t salt, ptls_iovec_t ikm) -{ - ptls_hash_context_t *hash; - - if (salt.len == 0) - salt = ptls_iovec_init(zeroes_of_max_digest_size, algo->digest_size); - - if ((hash = ptls_hmac_create(algo, salt.base, salt.len)) == NULL) - return PTLS_ERROR_NO_MEMORY; - hash->update(hash, ikm.base, ikm.len); - hash->final(hash, output, PTLS_HASH_FINAL_MODE_FREE); - return 0; -} - -int ptls_hkdf_expand(ptls_hash_algorithm_t *algo, void *output, size_t outlen, ptls_iovec_t prk, ptls_iovec_t info) -{ - ptls_hash_context_t *hmac = NULL; - size_t i; - uint8_t digest[PTLS_MAX_DIGEST_SIZE]; - - for (i = 0; (i * algo->digest_size) < outlen; ++i) { - if (hmac == NULL) { - if ((hmac = ptls_hmac_create(algo, prk.base, prk.len)) == NULL) - return PTLS_ERROR_NO_MEMORY; - } else { - hmac->update(hmac, digest, algo->digest_size); - } - hmac->update(hmac, info.base, info.len); - uint8_t gen = (uint8_t)(i + 1); - hmac->update(hmac, &gen, 1); - hmac->final(hmac, digest, 1); - - size_t off_start = i * algo->digest_size, off_end = off_start + algo->digest_size; - if (off_end > outlen) - off_end = outlen; - memcpy((uint8_t *)output + off_start, digest, off_end - off_start); - } - - if (hmac != NULL) - hmac->final(hmac, NULL, PTLS_HASH_FINAL_MODE_FREE); - - ptls_clear_memory(digest, algo->digest_size); - - return 0; -} - -int ptls_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 *base_label) -{ - ptls_buffer_t hkdf_label; - uint8_t hkdf_label_buf[512]; - int ret; - - ptls_buffer_init(&hkdf_label, hkdf_label_buf, sizeof(hkdf_label_buf)); - - ptls_buffer_push16(&hkdf_label, (uint16_t)outlen); - ptls_buffer_push_block(&hkdf_label, 1, { - if (base_label == NULL) - base_label = "tls13 "; - ptls_buffer_pushv(&hkdf_label, base_label, strlen(base_label)); - ptls_buffer_pushv(&hkdf_label, label, strlen(label)); - }); - ptls_buffer_push_block(&hkdf_label, 1, { ptls_buffer_pushv(&hkdf_label, hash_value.base, hash_value.len); }); - - ret = ptls_hkdf_expand(algo, output, outlen, secret, ptls_iovec_init(hkdf_label.base, hkdf_label.off)); - -Exit: - ptls_buffer_dispose(&hkdf_label); - return ret; -} - -ptls_cipher_context_t *ptls_cipher_new(ptls_cipher_algorithm_t *algo, int is_enc, const void *key) -{ - ptls_cipher_context_t *ctx; - - if ((ctx = (ptls_cipher_context_t *)malloc(algo->context_size)) == NULL) - return NULL; - *ctx = (ptls_cipher_context_t){algo}; - if (algo->setup_crypto(ctx, is_enc, key) != 0) { - free(ctx); - ctx = NULL; - } - return ctx; -} - -void ptls_cipher_free(ptls_cipher_context_t *ctx) -{ - ctx->do_dispose(ctx); - free(ctx); -} - -ptls_aead_context_t *ptls_aead_new(ptls_aead_algorithm_t *aead, ptls_hash_algorithm_t *hash, int is_enc, const void *secret, - const char *base_label) -{ - ptls_aead_context_t *ctx; - uint8_t key[PTLS_MAX_SECRET_SIZE]; - int ret; - - if ((ctx = (ptls_aead_context_t *)malloc(aead->context_size)) == NULL) - return NULL; - - *ctx = (ptls_aead_context_t){aead}; - if ((ret = get_traffic_key(hash, key, aead->key_size, 0, secret, base_label)) != 0) - goto Exit; - if ((ret = get_traffic_key(hash, ctx->static_iv, aead->iv_size, 1, secret, base_label)) != 0) - goto Exit; - ret = aead->setup_crypto(ctx, is_enc, key); - -Exit: - ptls_clear_memory(key, aead->key_size); - if (ret != 0) { - ptls_clear_memory(ctx->static_iv, aead->iv_size); - free(ctx); - ctx = NULL; - } - - return ctx; -} - -void ptls_aead_free(ptls_aead_context_t *ctx) -{ - ctx->dispose_crypto(ctx); - ptls_clear_memory(ctx->static_iv, ctx->algo->iv_size); - free(ctx); -} - -size_t ptls_aead_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq, const void *aad, - size_t aadlen) -{ - size_t off = 0; - - ptls_aead_encrypt_init(ctx, seq, aad, aadlen); - off += ptls_aead_encrypt_update(ctx, ((uint8_t *)output) + off, input, inlen); - off += ptls_aead_encrypt_final(ctx, ((uint8_t *)output) + off); - - return off; -} - -void ptls_aead__build_iv(ptls_aead_context_t *ctx, uint8_t *iv, uint64_t seq) -{ - size_t iv_size = ctx->algo->iv_size, i; - const uint8_t *s = ctx->static_iv; - uint8_t *d = iv; - - /* build iv */ - for (i = iv_size - 8; i != 0; --i) - *d++ = *s++; - i = 64; - do { - i -= 8; - *d++ = *s++ ^ (uint8_t)(seq >> i); - } while (i != 0); -} - -static void clear_memory(void *p, size_t len) -{ - if (len != 0) - memset(p, 0, len); -} - -void (*volatile ptls_clear_memory)(void *p, size_t len) = clear_memory; - -static uint64_t get_time(ptls_get_time_t *self) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return (uint64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000; -} - -ptls_get_time_t ptls_get_time = {get_time}; - |