Merge pull request #45 from h2o/master
Align to ESNI-03
diff --git a/include/picotls.h b/include/picotls.h
index 09bda7f..1054879 100644
--- a/include/picotls.h
+++ b/include/picotls.h
@@ -84,7 +84,10 @@
#define PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512 0x0806
/* ESNI */
-#define PTLS_ESNI_VERSION_DRAFT02 0xff01
+#define PTLS_ESNI_VERSION_DRAFT03 0xff02
+
+#define PTLS_ESNI_RESPONSE_TYPE_ACCEPT 0
+#define PTLS_ESNI_RESPONSE_TYPE_RETRY_REQUEST 1
/* error classes and macros */
#define PTLS_ERROR_CLASS_SELF_ALERT 0
@@ -132,6 +135,7 @@
#define PTLS_ERROR_STATELESS_RETRY (PTLS_ERROR_CLASS_INTERNAL + 6)
#define PTLS_ERROR_NOT_AVAILABLE (PTLS_ERROR_CLASS_INTERNAL + 7)
#define PTLS_ERROR_COMPRESSION_FAILURE (PTLS_ERROR_CLASS_INTERNAL + 8)
+#define PTLS_ERROR_ESNI_RETRY (PTLS_ERROR_CLASS_INTERNAL + 8)
#define PTLS_ERROR_INCORRECT_BASE64 (PTLS_ERROR_CLASS_INTERNAL + 50)
#define PTLS_ERROR_PEM_LABEL_NOT_FOUND (PTLS_ERROR_CLASS_INTERNAL + 51)
@@ -346,7 +350,7 @@
/**
* returns the digest and performs necessary operation specified by mode
*/
- void (* final)(struct st_ptls_hash_context_t *ctx, void *md, ptls_hash_final_mode_t mode);
+ void (*final)(struct st_ptls_hash_context_t *ctx, void *md, ptls_hash_final_mode_t mode);
/**
* creates a copy of the hash context
*/
@@ -403,8 +407,31 @@
uint16_t padded_length;
uint64_t not_before;
uint64_t not_after;
+ uint16_t version;
} ptls_esni_context_t;
+/**
+ * holds the ESNI secret, as exchanged during the handshake
+ */
+
+#define PTLS_ESNI_NONCE_SIZE 16
+
+typedef struct st_ptls_esni_secret_t {
+ ptls_iovec_t secret;
+ uint8_t nonce[PTLS_ESNI_NONCE_SIZE];
+ uint8_t esni_contents_hash[PTLS_MAX_DIGEST_SIZE];
+ union {
+ struct {
+ ptls_key_exchange_algorithm_t *key_share;
+ ptls_cipher_suite_t *cipher;
+ ptls_iovec_t pubkey;
+ uint8_t record_digest[PTLS_MAX_DIGEST_SIZE];
+ uint16_t padded_length;
+ } client;
+ };
+ uint16_t version;
+} ptls_esni_secret_t;
+
#define PTLS_CALLBACK_TYPE0(ret, name) \
typedef struct st_ptls_##name##_t { \
ret (*cb)(struct st_ptls_##name##_t * self); \
@@ -489,7 +516,8 @@
* event logging (incl. secret logging)
*/
typedef struct st_ptls_log_event_t {
- void (*cb)(struct st_ptls_log_event_t *self, ptls_t *tls, const char *type, const char *fmt, ...) __attribute__((format(printf, 4, 5)));
+ void (*cb)(struct st_ptls_log_event_t *self, ptls_t *tls, const char *type, const char *fmt, ...)
+ __attribute__((format(printf, 4, 5)));
} ptls_log_event_t;
/**
* reference counting
@@ -1154,6 +1182,12 @@
*
*/
void ptls_esni_dispose_context(ptls_esni_context_t *esni);
+
+/**
+ * Obtain the ESNI secrets negotiated during the handshake.
+ */
+ptls_esni_secret_t *ptls_get_esni_secret(ptls_t *ctx);
+
/**
*
*/
diff --git a/lib/openssl.c b/lib/openssl.c
index 6d503b5..5ce6e85 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -44,6 +44,10 @@
#include "picotls/openssl.h"
#ifdef _WINDOWS
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+#pragma warning(disable : 4996)
#include <ms\applink.c>
#endif
@@ -237,7 +241,7 @@
static int x9_62_create_key_exchange(ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **_ctx)
{
- EC_GROUP* group = NULL;
+ EC_GROUP *group = NULL;
struct st_x9_62_keyex_context_t *ctx = NULL;
int ret;
@@ -600,7 +604,6 @@
default:
return PTLS_ERROR_INCOMPATIBLE_KEY;
-
}
}
@@ -1217,7 +1220,8 @@
return ret;
}
-static void cleanup_cipher_ctx(EVP_CIPHER_CTX *ctx) {
+static void cleanup_cipher_ctx(EVP_CIPHER_CTX *ctx)
+{
if (!EVP_CIPHER_CTX_cleanup(ctx)) {
fprintf(stderr, "EVP_CIPHER_CTX_cleanup() failed\n");
abort();
diff --git a/lib/picotls.c b/lib/picotls.c
index b4a5705..a479bb8 100644
--- a/lib/picotls.c
+++ b/lib/picotls.c
@@ -38,8 +38,6 @@
#define PTLS_RECORD_VERSION_MAJOR 3
#define PTLS_RECORD_VERSION_MINOR 3
-#define PTLS_ESNI_NONCE_SIZE 16
-
#define PTLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC 20
#define PTLS_CONTENT_TYPE_ALERT 21
#define PTLS_CONTENT_TYPE_HANDSHAKE 22
@@ -127,21 +125,6 @@
struct st_ptls_signature_algorithms_t signature_algorithms;
};
-struct st_ptls_esni_secret_t {
- ptls_iovec_t secret;
- uint8_t nonce[PTLS_ESNI_NONCE_SIZE];
- uint8_t esni_contents_hash[PTLS_MAX_DIGEST_SIZE];
- union {
- struct {
- ptls_key_exchange_algorithm_t *key_share;
- ptls_cipher_suite_t *cipher;
- ptls_iovec_t pubkey;
- uint8_t record_digest[PTLS_MAX_DIGEST_SIZE];
- uint16_t padded_length;
- } client;
- };
-};
-
struct st_ptls_t {
/**
* the context
@@ -211,7 +194,7 @@
/**
* esni
*/
- struct st_ptls_esni_secret_t *esni;
+ ptls_esni_secret_t *esni;
/**
* exporter master secret (either 0rtt or 1rtt)
*/
@@ -458,26 +441,35 @@
#undef EXT
}
+#ifndef ntoh16
static uint16_t ntoh16(const uint8_t *src)
{
return (uint16_t)src[0] << 8 | src[1];
}
+#endif
+
+#ifndef ntoh24
static uint32_t ntoh24(const uint8_t *src)
{
return (uint32_t)src[0] << 16 | (uint32_t)src[1] << 8 | src[2];
}
+#endif
+#ifndef ntoh32
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];
}
+#endif
+#ifndef ntoh64
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];
}
+#endif
void ptls_buffer__release_memory(ptls_buffer_t *buf)
{
@@ -1583,9 +1575,9 @@
return ret;
}
-static int parse_esni_keys(ptls_context_t *ctx, ptls_key_exchange_algorithm_t **selected_key_share,
+static int parse_esni_keys(ptls_context_t *ctx, uint16_t *esni_version, ptls_key_exchange_algorithm_t **selected_key_share,
ptls_cipher_suite_t **selected_cipher, ptls_iovec_t *peer_key, uint16_t *padded_length,
- ptls_iovec_t input)
+ char **published_sni, ptls_iovec_t input)
{
const uint8_t *src = input.base, *const end = input.base + input.len;
uint16_t version;
@@ -1595,7 +1587,7 @@
/* version */
if ((ret = ptls_decode16(&version, &src, end)) != 0)
goto Exit;
- if (version != PTLS_ESNI_VERSION_DRAFT02) {
+ if (version != PTLS_ESNI_VERSION_DRAFT03) {
ret = PTLS_ALERT_DECODE_ERROR;
goto Exit;
}
@@ -1621,6 +1613,21 @@
}
src += 4;
}
+ *esni_version = version;
+ /* published sni */
+ ptls_decode_open_block(src, end, 2, {
+ size_t len = end - src;
+ *published_sni = malloc(len + 1);
+ if (*published_sni == NULL) {
+ ret = PTLS_ERROR_NO_MEMORY;
+ goto Exit;
+ }
+ if (len > 0) {
+ memcpy(*published_sni, src, len);
+ }
+ (*published_sni)[len] = 0;
+ src = end;
+ });
/* key-shares */
ptls_decode_open_block(src, end, 2, {
if ((ret = select_key_share(selected_key_share, peer_key, ctx->key_exchanges, &src, end, 0)) != 0)
@@ -1706,7 +1713,7 @@
return ret;
}
-static void free_esni_secret(struct st_ptls_esni_secret_t **esni, int is_server)
+static void free_esni_secret(ptls_esni_secret_t **esni, int is_server)
{
assert(*esni != NULL);
if ((*esni)->secret.base != NULL) {
@@ -1720,7 +1727,7 @@
*esni = NULL;
}
-static int client_setup_esni(ptls_context_t *ctx, struct st_ptls_esni_secret_t **esni, ptls_iovec_t esni_keys,
+static int client_setup_esni(ptls_context_t *ctx, ptls_esni_secret_t **esni, ptls_iovec_t esni_keys, char **published_sni,
const uint8_t *client_random)
{
ptls_iovec_t peer_key;
@@ -1731,8 +1738,8 @@
memset(*esni, 0, sizeof(**esni));
/* parse ESNI_Keys (and return success while keeping *esni NULL) */
- if (parse_esni_keys(ctx, &(*esni)->client.key_share, &(*esni)->client.cipher, &peer_key, &(*esni)->client.padded_length,
- esni_keys) != 0) {
+ if (parse_esni_keys(ctx, &(*esni)->version, &(*esni)->client.key_share, &(*esni)->client.cipher, &peer_key,
+ &(*esni)->client.padded_length, published_sni, esni_keys) != 0) {
free(*esni);
*esni = NULL;
return 0;
@@ -1759,7 +1766,7 @@
return ret;
}
-static int emit_esni_extension(struct st_ptls_esni_secret_t *esni, ptls_buffer_t *buf, ptls_iovec_t esni_keys,
+static int emit_esni_extension(ptls_esni_secret_t *esni, ptls_buffer_t *buf, ptls_iovec_t esni_keys,
const char *server_name, size_t key_share_ch_off, size_t key_share_ch_len)
{
ptls_aead_context_t *aead = NULL;
@@ -1810,6 +1817,7 @@
ptls_iovec_t *cookie)
{
ptls_iovec_t resumption_secret = {NULL}, resumption_ticket;
+ char *published_sni = NULL;
uint32_t obfuscated_ticket_age = 0;
size_t msghash_off;
uint8_t binder_key[PTLS_MAX_DIGEST_SIZE];
@@ -1819,8 +1827,10 @@
if (properties != NULL) {
/* try to use ESNI */
if (!is_second_flight && send_sni && properties->client.esni_keys.base != NULL) {
- if ((ret = client_setup_esni(tls->ctx, &tls->esni, properties->client.esni_keys, tls->client_random)) != 0)
+ if ((ret = client_setup_esni(tls->ctx, &tls->esni, properties->client.esni_keys, &published_sni, tls->client_random)) !=
+ 0) {
goto Exit;
+ }
if (tls->ctx->update_esni_key != NULL) {
if ((ret = tls->ctx->update_esni_key->cb(tls->ctx->update_esni_key, tls, tls->esni->secret,
tls->esni->client.cipher->hash, tls->esni->esni_contents_hash)) != 0)
@@ -1905,6 +1915,12 @@
});
if (send_sni) {
if (tls->esni != NULL) {
+ if (published_sni != NULL) {
+ buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_NAME, {
+ if ((ret = emit_server_name_extension(sendbuf, published_sni)) != 0)
+ goto Exit;
+ });
+ }
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_SERVER_NAME, {
if ((ret = emit_esni_extension(tls->esni, sendbuf, properties->client.esni_keys, tls->server_name,
key_share_client_hello.off, key_share_client_hello.len)) != 0)
@@ -2024,6 +2040,9 @@
ret = PTLS_ERROR_IN_PROGRESS;
Exit:
+ if (published_sni != NULL) {
+ free(published_sni);
+ }
ptls_clear_memory(binder_key, sizeof(binder_key));
return ret;
}
@@ -2183,8 +2202,8 @@
tls->client.key_share_ctx = NULL;
}
if (tls->client.using_early_data) {
- /* release traffic encryption key so that 2nd CH goes out in cleartext, but keep the epoch at 1 since we've already called
- * derive-secret */
+ /* release traffic encryption key so that 2nd CH goes out in cleartext, but keep the epoch at 1 since we've already
+ * called derive-secret */
if (tls->ctx->update_traffic_key == NULL) {
assert(tls->traffic_protection.enc.aead != NULL);
ptls_aead_free(tls->traffic_protection.enc.aead);
@@ -2284,7 +2303,6 @@
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) {
@@ -2313,7 +2331,7 @@
static int client_handle_encrypted_extensions(ptls_t *tls, ptls_iovec_t message, ptls_handshake_properties_t *properties)
{
- const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len, *esni = NULL;
+ const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len, *esni_nonce = NULL;
uint16_t type;
ptls_raw_extension_t unknown_extensions[MAX_UNKNOWN_EXTENSIONS + 1];
int ret, skip_early_data = 1;
@@ -2337,11 +2355,17 @@
}
break;
case PTLS_EXTENSION_TYPE_ENCRYPTED_SERVER_NAME:
- if (end - src != PTLS_ESNI_NONCE_SIZE) {
- ret = PTLS_ALERT_ILLEGAL_PARAMETER;
+ if (*src == PTLS_ESNI_RESPONSE_TYPE_ACCEPT) {
+ if (end - src != PTLS_ESNI_NONCE_SIZE + 1) {
+ ret = PTLS_ALERT_ILLEGAL_PARAMETER;
+ goto Exit;
+ }
+ esni_nonce = src + 1;
+ } else {
+ /* TODO: provide API to parse the RETRY REQUEST response */
+ ret = PTLS_ERROR_ESNI_RETRY;
goto Exit;
}
- esni = src;
break;
case PTLS_EXTENSION_TYPE_ALPN:
ptls_decode_block(src, end, 2, {
@@ -2375,13 +2399,13 @@
});
if (tls->esni != NULL) {
- if (esni == NULL || !ptls_mem_equal(esni, tls->esni->nonce, PTLS_ESNI_NONCE_SIZE)) {
+ if (esni_nonce == NULL || !ptls_mem_equal(esni_nonce, tls->esni->nonce, PTLS_ESNI_NONCE_SIZE)) {
ret = PTLS_ALERT_ILLEGAL_PARAMETER;
goto Exit;
}
free_esni_secret(&tls->esni, 0);
} else {
- if (esni != NULL) {
+ if (esni_nonce != NULL) {
ret = PTLS_ALERT_ILLEGAL_PARAMETER;
goto Exit;
}
@@ -2889,7 +2913,7 @@
return ret;
}
-static int client_hello_decrypt_esni(ptls_context_t *ctx, ptls_iovec_t *server_name, struct st_ptls_esni_secret_t **secret,
+static int client_hello_decrypt_esni(ptls_context_t *ctx, ptls_iovec_t *server_name, ptls_esni_secret_t **secret,
struct st_ptls_client_hello_t *ch)
{
ptls_esni_context_t **esni;
@@ -2914,8 +2938,10 @@
ret = PTLS_ALERT_ILLEGAL_PARAMETER;
goto Exit;
}
- if (memcmp((*esni)->cipher_suites[i].record_digest, ch->esni.record_digest, ch->esni.cipher->hash->digest_size) == 0)
+ if (memcmp((*esni)->cipher_suites[i].record_digest, ch->esni.record_digest, ch->esni.cipher->hash->digest_size) == 0) {
+ (*secret)->version = (*esni)->version;
break;
+ }
}
if (*esni == NULL) {
ret = PTLS_ALERT_ILLEGAL_PARAMETER;
@@ -3505,8 +3531,8 @@
additional_extensions \
} while (0); \
})
- struct st_ptls_client_hello_t ch = {NULL, {NULL}, {NULL}, 0, {NULL}, {NULL}, {NULL}, {{0}},
- {NULL}, {NULL}, {{{NULL}}}, {{0}}, {{0}}, {{NULL}}, {NULL}, {{UINT16_MAX}}};
+ struct st_ptls_client_hello_t ch = {NULL, {NULL}, {NULL}, 0, {NULL}, {NULL}, {NULL}, {{0}},
+ {NULL}, {NULL}, {{{NULL}}}, {{0}}, {{0}}, {{NULL}}, {NULL}, {{UINT16_MAX}}};
struct {
ptls_key_exchange_algorithm_t *algorithm;
ptls_iovec_t peer_key;
@@ -3561,14 +3587,14 @@
ret = PTLS_ALERT_DECODE_ERROR;
goto Exit;
}
- /* the following check is necessary so that we would be able to track the connection in SSLKEYLOGFILE, even though it might
- * not be for the safety of the protocol */
+ /* the following check is necessary so that we would be able to track the connection in SSLKEYLOGFILE, even though it
+ * might not be for the safety of the protocol */
if (!ptls_mem_equal(tls->client_random, ch.random_bytes, sizeof(tls->client_random))) {
ret = PTLS_ALERT_HANDSHAKE_FAILURE;
goto Exit;
}
- /* We compare SNI only when the value is saved by the on_client_hello callback. This should be OK because we are ignoring
- * the value unless the callback saves the server-name. */
+ /* We compare SNI only when the value is saved by the on_client_hello callback. This should be OK because we are
+ * ignoring the value unless the callback saves the server-name. */
if (tls->server_name != NULL) {
size_t l = strlen(tls->server_name);
if (!(ch.server_name.len == l && memcmp(ch.server_name.base, tls->server_name, l) == 0)) {
@@ -3642,7 +3668,8 @@
ptls__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 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, {
ptls_buffer_t *sendbuf = emitter->buf;
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_COOKIE, {
@@ -3805,14 +3832,17 @@
ptls_buffer_t *sendbuf = emitter->buf;
ptls_buffer_push_block(sendbuf, 2, {
if (tls->esni != NULL) {
- /* the extension is sent even if the application does not handle server name, because otherwise the handshake would
- * fail (FIXME ch.esni.nonce will be zero on HRR) */
- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_SERVER_NAME,
- { ptls_buffer_pushv(sendbuf, tls->esni->nonce, PTLS_ESNI_NONCE_SIZE); });
+ /* the extension is sent even if the application does not handle server name, because otherwise the handshake
+ * would fail (FIXME ch.esni.nonce will be zero on HRR) */
+ buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_SERVER_NAME, {
+ uint8_t response_type = PTLS_ESNI_RESPONSE_TYPE_ACCEPT;
+ ptls_buffer_pushv(sendbuf, &response_type, 1);
+ ptls_buffer_pushv(sendbuf, tls->esni->nonce, PTLS_ESNI_NONCE_SIZE);
+ });
free_esni_secret(&tls->esni, 1);
} else 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) */
+ /* 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) {
@@ -4860,7 +4890,8 @@
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 *label_prefix)
{
- /* the handshake layer should call hkdf_expand_label directly, always setting key_schedule->hkdf_label_prefix as the argument */
+ /* the handshake layer should call hkdf_expand_label directly, always setting key_schedule->hkdf_label_prefix as the
+ * argument */
if (label_prefix == NULL)
label_prefix = PTLS_HKDF_EXPAND_LABEL_PREFIX;
return hkdf_expand_label(algo, output, outlen, secret, label, hash_value, label_prefix);
@@ -5088,11 +5119,18 @@
memcpy(esni->key_exchanges, key_exchanges, sizeof(*esni->key_exchanges) * (num_key_exchanges + 1));
/* ESNIKeys */
- if (end - src < 6) {
+ if ((ret = ptls_decode16(&esni->version, &src, end)) != 0)
+ goto Exit;
+ /* Skip checksum fields */
+ if (end - src < 4) {
ret = PTLS_ALERT_DECRYPT_ERROR;
goto Exit;
}
- src += 6;
+ src += 4;
+ /* Published SNI field */
+ ptls_decode_open_block(src, end, 2, { src = end; });
+
+ /* Process the list of KeyShareEntries, verify for each of them that the ciphersuite is supported. */
ptls_decode_open_block(src, end, 2, {
do {
/* parse */
@@ -5111,6 +5149,7 @@
}
} while (src != end);
});
+ /* Process the list of cipher_suites. If they are supported, store in esni context */
ptls_decode_open_block(src, end, 2, {
void *newp;
do {
@@ -5137,12 +5176,14 @@
esni->cipher_suites = newp;
esni->cipher_suites[num_cipher_suites].cipher_suite = NULL;
});
+ /* Parse the padded length, not before, not after parameters */
if ((ret = ptls_decode16(&esni->padded_length, &src, end)) != 0)
goto Exit;
if ((ret = ptls_decode64(&esni->not_before, &src, end)) != 0)
goto Exit;
if ((ret = ptls_decode64(&esni->not_after, &src, end)) != 0)
goto Exit;
+ /* Skip the extension fields */
ptls_decode_block(src, end, 2, {
while (src != end) {
uint16_t ext_type;
@@ -5181,6 +5222,14 @@
}
/**
+ * Obtain the ESNI secrets negotiated during the handshake.
+ */
+ptls_esni_secret_t *ptls_get_esni_secret(ptls_t *ctx)
+{
+ return ctx->esni;
+}
+
+/**
* checks if given name looks like an IP address
*/
int ptls_server_name_is_ipaddr(const char *name)
diff --git a/picotlsvs/picotls-esni/getopt.c b/picotlsvs/picotls-esni/getopt.c
new file mode 100644
index 0000000..21cf6ca
--- /dev/null
+++ b/picotlsvs/picotls-esni/getopt.c
@@ -0,0 +1,103 @@
+#include "getopt.h"
+
+/*
+* Copyright (c) 1987, 1993, 1994
+* The Regents of the University of California. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+* 3. All advertising materials mentioning features or use of this software
+* must display the following acknowledgement:
+* This product includes software developed by the University of
+* California, Berkeley and its contributors.
+* 4. Neither the name of the University nor the names of its contributors
+* may be used to endorse or promote products derived from this software
+* without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+* SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+int opterr = 1, /* if error message should be printed */
+ optind = 1, /* index into parent argv vector */
+ optopt, /* character checked for validity */
+ optreset; /* reset getopt */
+const char* optarg; /* argument associated with option */
+
+#define BADCH (int)'?'
+#define BADARG (int)':'
+#define EMSG ""
+
+/*
+ * getopt --
+ * Parse argc/argv argument vector.
+ */
+int getopt(int nargc, char* const nargv[], const char* ostr)
+{
+ static const char* place = EMSG; /* option letter processing */
+ const char* oli; /* option letter list index */
+
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc || *(place = nargv[optind]) != '-') {
+ place = EMSG;
+ return (-1);
+ }
+ if (place[1] && *++place == '-') { /* found "--" */
+ ++optind;
+ place = EMSG;
+ return (-1);
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, optopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means -1.
+ */
+ if (optopt == (int)'-')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (opterr && *ostr != ':')
+ (void)printf("illegal option -- %c\n", optopt);
+ return (BADCH);
+ }
+ if (*++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ } else { /* need an argument */
+ if (*place) /* no white space */
+ optarg = place;
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ if (*ostr == ':')
+ return (BADARG);
+ if (opterr)
+ (void)printf("option requires an argument -- %c\n", optopt);
+ return (BADCH);
+ } else /* white space */
+ optarg = nargv[optind];
+ place = EMSG;
+ ++optind;
+ }
+ return (optopt); /* dump back option letter */
+}
diff --git a/picotlsvs/picotls-esni/getopt.h b/picotlsvs/picotls-esni/getopt.h
new file mode 100644
index 0000000..1ea538e
--- /dev/null
+++ b/picotlsvs/picotls-esni/getopt.h
@@ -0,0 +1,19 @@
+#ifndef GETOPT_H
+#ifndef __APPLE__
+
+#define GETOPT_H
+
+#ifndef _GETOPT_H
+#define _GETOPT_H
+#endif
+
+extern int opterr; /* if error message should be printed */
+extern int optind; /* index into parent argv vector */
+extern int optopt; /* character checked for validity */
+extern int optreset; /* reset getopt */
+extern const char* optarg; /* argument associated with option */
+
+int getopt(int nargc, char* const nargv[], const char* ostr);
+
+#endif
+#endif
diff --git a/picotlsvs/picotls-esni/picotls-esni.vcxproj b/picotlsvs/picotls-esni/picotls-esni.vcxproj
new file mode 100644
index 0000000..9e4678f
--- /dev/null
+++ b/picotlsvs/picotls-esni/picotls-esni.vcxproj
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\src\esni.c" />
+ <ClCompile Include="getopt.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="getopt.h" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{592127C5-DD8C-47ED-8EBA-026B5848C374}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>picotlsesni</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(OPENSSL64DIR)\include;$(ProjectDir)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>$(OPENSSL64DIR);$(OPENSSL64DIR)\lib;$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>picotls.lib;libcrypto.lib;libssl.lib;microecc.lib;cifra.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ <AdditionalIncludeDirectories>$(OPENSSLDIR)\include;$(ProjectDir)..\..\include;$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>$(OPENSSLDIR);$(OPENSSLDIR)\lib;$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>picotls.lib;libcrypto.lib;libssl.lib;microecc.lib;cifra.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ <AdditionalIncludeDirectories>$(OPENSSLDIR)\include;$(ProjectDir)..\..\include;$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>$(OPENSSLDIR);$(OPENSSLDIR)\lib;$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>picotls.lib;libcrypto.lib;libssl.lib;microecc.lib;cifra.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(OPENSSL64DIR)\include;$(ProjectDir)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>$(OPENSSL64DIR);$(OPENSSL64DIR)\lib;$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>picotls.lib;libcrypto.lib;libssl.lib;microecc.lib;cifra.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/picotlsvs/picotls-esni/picotls-esni.vcxproj.filters b/picotlsvs/picotls-esni/picotls-esni.vcxproj.filters
new file mode 100644
index 0000000..2ad14fc
--- /dev/null
+++ b/picotlsvs/picotls-esni/picotls-esni.vcxproj.filters
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\src\esni.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="getopt.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="getopt.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/picotlsvs/picotls-esni/picotls-esni.vcxproj.user b/picotlsvs/picotls-esni/picotls-esni.vcxproj.user
new file mode 100644
index 0000000..1bac74a
--- /dev/null
+++ b/picotlsvs/picotls-esni/picotls-esni.vcxproj.user
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LocalDebuggerCommandArguments>-K ..\..\..\..\picoquic\certs\esni-secp256r1.key -a rr.txt > rr.bin</LocalDebuggerCommandArguments>
+ <LocalDebuggerWorkingDirectory>$(OutDir)</LocalDebuggerWorkingDirectory>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+ </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/picotlsvs/picotls/wincompat.h b/picotlsvs/picotls/wincompat.h
index 4a7b2f5..8c9c084 100644
--- a/picotlsvs/picotls/wincompat.h
+++ b/picotlsvs/picotls/wincompat.h
@@ -14,24 +14,24 @@
#define __attribute__(X)
#endif
-#ifdef __cplusplus
+#ifdef __cplusplus
extern "C" {
#endif
- struct timezone {
- int tz_minuteswest; /* minutes west of Greenwich */
- int tz_dsttime; /* type of DST correction */
- };
+struct timezone {
+ int tz_minuteswest; /* minutes west of Greenwich */
+ int tz_dsttime; /* type of DST correction */
+};
- int wintimeofday(struct timeval* tv, struct timezone* tz);
+int wintimeofday(struct timeval *tv, struct timezone *tz);
-#ifdef __cplusplus
+#ifndef strcasecmp
+#define strcasecmp _stricmp
+#endif
+
+#ifdef __cplusplus
} /* extern "C" */
#endif
-
-
-
#endif
-
#endif /* WINCOMPAT_H */
\ No newline at end of file
diff --git a/picotlsvs/picotlsvs.sln b/picotlsvs/picotlsvs.sln
index e812b84..39a4171 100644
--- a/picotlsvs/picotlsvs.sln
+++ b/picotlsvs/picotlsvs.sln
@@ -30,6 +30,13 @@
{3440FDEA-84D2-4424-BB19-B4B26A6ADC8A} = {3440FDEA-84D2-4424-BB19-B4B26A6ADC8A}
EndProjectSection
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "picotls-esni", "picotls-esni\picotls-esni.vcxproj", "{592127C5-DD8C-47ED-8EBA-026B5848C374}"
+ ProjectSection(ProjectDependencies) = postProject
+ {5D4DA3A3-7851-4CAE-AE4F-C421A2C8C440} = {5D4DA3A3-7851-4CAE-AE4F-C421A2C8C440}
+ {46E6D6E9-7A30-4058-9661-DF70CC07E821} = {46E6D6E9-7A30-4058-9661-DF70CC07E821}
+ {3440FDEA-84D2-4424-BB19-B4B26A6ADC8A} = {3440FDEA-84D2-4424-BB19-B4B26A6ADC8A}
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -86,6 +93,14 @@
{15D7D32F-3B62-4B10-A06A-BA1ADD591D7F}.Release|x64.Build.0 = Release|x64
{15D7D32F-3B62-4B10-A06A-BA1ADD591D7F}.Release|x86.ActiveCfg = Release|Win32
{15D7D32F-3B62-4B10-A06A-BA1ADD591D7F}.Release|x86.Build.0 = Release|Win32
+ {592127C5-DD8C-47ED-8EBA-026B5848C374}.Debug|x64.ActiveCfg = Debug|x64
+ {592127C5-DD8C-47ED-8EBA-026B5848C374}.Debug|x64.Build.0 = Debug|x64
+ {592127C5-DD8C-47ED-8EBA-026B5848C374}.Debug|x86.ActiveCfg = Debug|Win32
+ {592127C5-DD8C-47ED-8EBA-026B5848C374}.Debug|x86.Build.0 = Debug|Win32
+ {592127C5-DD8C-47ED-8EBA-026B5848C374}.Release|x64.ActiveCfg = Release|x64
+ {592127C5-DD8C-47ED-8EBA-026B5848C374}.Release|x64.Build.0 = Release|x64
+ {592127C5-DD8C-47ED-8EBA-026B5848C374}.Release|x86.ActiveCfg = Release|Win32
+ {592127C5-DD8C-47ED-8EBA-026B5848C374}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/esni.c b/src/esni.c
index 76f47f4..5c5c047 100644
--- a/src/esni.c
+++ b/src/esni.c
@@ -24,7 +24,15 @@
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
+#ifdef _WINDOWS
+#include "..\picotls\wincompat.h"
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+#pragma warning(disable : 4996)
+#else
#include <strings.h>
+#endif
#include <time.h>
#include <openssl/err.h>
#include <openssl/engine.h>
@@ -34,7 +42,7 @@
#include "picotls/openssl.h"
static int emit_esni(ptls_key_exchange_context_t **key_exchanges, ptls_cipher_suite_t **cipher_suites, uint16_t padded_length,
- uint64_t not_before, uint64_t lifetime)
+ uint64_t not_before, uint64_t lifetime, char const *published_sni, char const *file_output)
{
ptls_buffer_t buf;
ptls_key_exchange_context_t *ctx[256] = {NULL};
@@ -42,8 +50,13 @@
ptls_buffer_init(&buf, "", 0);
- ptls_buffer_push16(&buf, PTLS_ESNI_VERSION_DRAFT02);
+ ptls_buffer_push16(&buf, PTLS_ESNI_VERSION_DRAFT03);
ptls_buffer_push(&buf, 0, 0, 0, 0); /* checksum, filled later */
+ if (published_sni != NULL) {
+ ptls_buffer_push_block(&buf, 2, { ptls_buffer_pushv(&buf, published_sni, strlen(published_sni)); });
+ } else {
+ ptls_buffer_push16(&buf, 0);
+ }
ptls_buffer_push_block(&buf, 2, {
size_t i;
for (i = 0; key_exchanges[i] != NULL; ++i) {
@@ -67,9 +80,20 @@
memcpy(buf.base + 2, d, 4);
}
- /* emit the structure to stdout */
- fwrite(buf.base, 1, buf.off, stdout);
- fflush(stdout);
+ if (file_output != NULL) {
+ FILE *fo = fopen(file_output, "wb");
+ if (fo == NULL) {
+ fprintf(stderr, "failed to open file:%s:%s\n", optarg, strerror(errno));
+ goto Exit;
+ } else {
+ fwrite(buf.base, 1, buf.off, fo);
+ fclose(fo);
+ }
+ } else {
+ /* emit the structure to stdout */
+ fwrite(buf.base, 1, buf.off, stdout);
+ fflush(stdout);
+ }
ret = 0;
Exit : {
@@ -87,11 +111,14 @@
"\n"
"Usage: %s [options]\n"
"Options:\n"
+ " -n <published-sni> published sni value\n"
" -K <key-file> private key files (repeat the option to include multiple\n"
" keys)\n"
" -c <cipher-suite> aes128-gcm, chacha20-poly1305, ...\n"
" -d <days> number of days until expiration (default: 90)\n"
" -p <padded-length> padded length (default: 260)\n"
+ " -o <output-file> write output to specified file instead of stdout\n"
+ " (use on Windows as stdout is not binary there)\n"
" -h prints this help\n"
"\n"
"-c and -x can be used multiple times.\n"
@@ -102,6 +129,8 @@
int main(int argc, char **argv)
{
+ char const *published_sni = NULL;
+ char const *file_output = NULL;
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
#if !defined(OPENSSL_NO_ENGINE)
@@ -123,15 +152,21 @@
uint64_t lifetime = 90 * 86400;
int ch;
- while ((ch = getopt(argc, argv, "K:c:d:p:h")) != -1) {
+
+ while ((ch = getopt(argc, argv, "n:K:c:d:p:o:h")) != -1) {
switch (ch) {
+ case 'n':
+ published_sni = optarg;
+ break;
case 'K': {
FILE *fp;
EVP_PKEY *pkey;
+
if ((fp = fopen(optarg, "rt")) == NULL) {
fprintf(stderr, "failed to open file:%s:%s\n", optarg, strerror(errno));
exit(1);
}
+
if ((pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)) == NULL) {
fprintf(stderr, "failed to read private key from file:%s:%s\n", optarg, strerror(errno));
exit(1);
@@ -162,10 +197,20 @@
lifetime *= 86400; /* convert to seconds */
break;
case 'p':
+#ifdef _WINDOWS
+ if (sscanf_s(optarg, "%" SCNu16, &padded_length) != 1 || padded_length == 0) {
+ fprintf(stderr, "padded length must be a positive integer\n");
+ exit(1);
+ }
+#else
if (sscanf(optarg, "%" SCNu16, &padded_length) != 1 || padded_length == 0) {
fprintf(stderr, "padded length must be a positive integer\n");
exit(1);
}
+#endif
+ break;
+ case 'o':
+ file_output = optarg;
break;
case 'h':
usage(argv[0], 0);
@@ -185,7 +230,8 @@
argc -= optind;
argv += optind;
- if (emit_esni(key_exchanges.elements, cipher_suites.elements, padded_length, time(NULL), lifetime) != 0) {
+ if (emit_esni(key_exchanges.elements, cipher_suites.elements, padded_length, time(NULL), lifetime, published_sni,
+ file_output) != 0) {
fprintf(stderr, "failed to generate ESNI private structure.\n");
exit(1);
}
diff --git a/t/test.h b/t/test.h
index f1d95ba..0d35832 100644
--- a/t/test.h
+++ b/t/test.h
@@ -53,10 +53,12 @@
/* secp256r1 key that lasts until 2028 */
#define ESNIKEYS \
- "\xff\x01\xba\xd5\xad\xa2\x00\x45\x00\x17\x00\x41\x04\x3e\xee\xf7\x10\xe3\x75\x07\xa8\xfb\x3e\xfc\x62\x50\x24\x95\xa0\x61\x6e" \
- "\xff\x6b\x63\x0f\xa3\xfd\xcc\x33\x36\xd0\xb1\x2d\x55\xba\xb0\x06\xbd\xb4\x29\x82\xc6\xd9\xee\x66\x84\xa9\x63\x94\x44\xbe\x04" \
- "\xe7\xee\xcf\xab\xc2\xc9\xdd\x40\xe6\xc8\x89\x88\xed\x94\x86\x00\x04\x13\x01\x13\x03\x01\x04\x00\x00\x00\x00\x5c\x13\x5e\xd2" \
- "\x00\x00\x00\x00\x6e\xdf\x61\xd1\x00\x00"
+ "\xff\x02\xcf\x27\xde\x17\x00\x0b\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x00\x45" \
+ "\x00\x17\x00\x41\x04\x3e\xee\xf7\x10\xe3\x75\x07\xa8\xfb\x3e\xfc\x62\x50\x24\x95\xa0" \
+ "\x61\x6e\xff\x6b\x63\x0f\xa3\xfd\xcc\x33\x36\xd0\xb1\x2d\x55\xba\xb0\x06\xbd\xb4\x29" \
+ "\x82\xc6\xd9\xee\x66\x84\xa9\x63\x94\x44\xbe\x04\xe7\xee\xcf\xab\xc2\xc9\xdd\x40\xe6" \
+ "\xc8\x89\x88\xed\x94\x86\x00\x02\x13\x01\x01\x04\x00\x00\x00\x00\x5d\x1c\xc0\x63\x00" \
+ "\x00\x4e\x94\xee\x6b\xc0\x62\x00\x00"
#define ESNI_SECP256R1KEY \
"-----BEGIN EC PARAMETERS-----\nBggqhkjOPQMBBw==\n-----END EC PARAMETERS-----\n-----BEGIN EC PRIVATE " \
"KEY-----\nMHcCAQEEIGrRVTfTXuOVewLt/g+Ugvg9XW/g4lGXrkZ8fdYaYuJCoAoGCCqGSM49\nAwEHoUQDQgAEPu73EON1B6j7PvxiUCSVoGFu/" \