it works (for the most straight-forward case)
diff --git a/include/picotls.h b/include/picotls.h
index 80f9e05..9abdc6d 100644
--- a/include/picotls.h
+++ b/include/picotls.h
@@ -717,6 +717,13 @@
int (*cb)(struct st_ptls_decompress_certificate_t *self, ptls_t *tls, uint16_t algorithm, ptls_iovec_t output,
ptls_iovec_t input);
} ptls_decompress_certificate_t;
+/**
+ * ECH: creates the AEAD context to be used for "Open"-ing inner CH. Given `condig_id`, the callback looks up the ECH config and the
+ * corresponding private key, invokes `ptls_hpke_setup_base_r` with provided `cipher`, `enc`, and `info_prefix` (which will be
+ * "tls ech" || 00).
+ */
+PTLS_CALLBACK_TYPE(ptls_aead_context_t *, ech_create_opener, ptls_t *tls, uint8_t config_id, ptls_hpke_cipher_suite_t *cipher,
+ ptls_iovec_t enc, ptls_iovec_t info_prefix);
/**
* the configuration
@@ -751,6 +758,7 @@
struct {
ptls_hpke_kem_t **kems;
ptls_hpke_cipher_suite_t **ciphers;
+ ptls_ech_create_opener_t *create_opener;
} ech;
/**
*
diff --git a/lib/picotls.c b/lib/picotls.c
index 75749d5..81bc646 100644
--- a/lib/picotls.c
+++ b/lib/picotls.c
@@ -74,6 +74,10 @@
#define PTLS_SERVER_NAME_TYPE_HOSTNAME 0
#define PTLS_ECH_CONFIG_VERSION 0xfe0d
+#define PTLS_ECH_CLIENT_HELLO_TYPE_OUTER 0
+#define PTLS_ECH_CLIENT_HELLO_TYPE_INNER 1
+
+static const ptls_iovec_t ech_info_prefix = {(uint8_t *)"tls ech", 8};
#define PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING "TLS 1.3, server CertificateVerify"
#define PTLS_CLIENT_CERTIFICATE_VERIFY_CONTEXT_STRING "TLS 1.3, client CertificateVerify"
@@ -331,6 +335,21 @@
unsigned sent_key_share : 1;
} cookie;
struct {
+ uint8_t list[MAX_CERTIFICATE_TYPES];
+ size_t count;
+ } server_certificate_types;
+ unsigned status_request : 1;
+ /**
+ * ECH: payload.base != NULL indicates that the extension was received
+ */
+ struct {
+ uint8_t type;
+ uint8_t config_id;
+ ptls_hpke_cipher_suite_t *cipher;
+ ptls_iovec_t enc;
+ ptls_iovec_t payload;
+ } ech;
+ struct {
const uint8_t *hash_end;
struct {
struct st_ptls_client_hello_psk_t list[4];
@@ -340,12 +359,8 @@
unsigned early_data_indication : 1;
unsigned is_last_extension : 1;
} psk;
- struct {
- uint8_t list[MAX_CERTIFICATE_TYPES];
- size_t count;
- } server_certificate_types;
ptls_raw_extension_t unknown_extensions[MAX_UNKNOWN_EXTENSIONS + 1];
- unsigned status_request : 1;
+ size_t first_extension_at;
};
struct st_ptls_server_hello_t {
@@ -932,7 +947,6 @@
static void client_free_ech(struct st_ptls_ech_client_t *ech)
{
- free(ech->public_name);
free(ech->enc.base);
if (ech->aead != NULL)
ptls_aead_free(ech->aead);
@@ -1037,8 +1051,7 @@
memcpy(ech->public_name, public_name.base, public_name.len);
ech->public_name[public_name.len] = '\0';
- ptls_buffer_pushv(&infobuf, "tls ech", 7);
- ptls_buffer_push(&infobuf, 0);
+ ptls_buffer_pushv(&infobuf, ech_info_prefix.base, ech_info_prefix.len);
ptls_buffer_pushv(&infobuf, ech_config.base, ech_config.len);
ret = ptls_hpke_setup_base_s(kem, cipher, &ech->enc, &ech->aead, public_key, ptls_iovec_init(infobuf.base, infobuf.off));
@@ -1065,13 +1078,13 @@
ptls_decode_block(src, end, 2, {
do {
+ const uint8_t *config_start = src;
uint16_t version;
if ((ret = ptls_decode16(&version, &src, end)) != 0)
goto Exit;
ptls_decode_open_block(src, end, 2, {
/* If the block is the one that we recognize, parse it, then adopt if if possible. Otherwise, skip. */
if (version == PTLS_ECH_CONFIG_VERSION) {
- const uint8_t *config_start = src;
uint8_t config_id;
ptls_hpke_kem_t *kem;
ptls_iovec_t public_key;
@@ -1966,23 +1979,27 @@
if (ech != NULL) {
if (mode == ENCODE_CH_MODE_OUTER) {
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO, {
- ptls_buffer_push(sendbuf, 0);
+ ptls_buffer_push(sendbuf, PTLS_ECH_CLIENT_HELLO_TYPE_OUTER, ech->config_id);
ptls_buffer_push16(sendbuf, ech->cipher->id.kdf);
ptls_buffer_push16(sendbuf, ech->cipher->id.aead);
ptls_buffer_push_block(sendbuf, 2, { ptls_buffer_pushv(sendbuf, ech->enc.base, ech->enc.len); });
- if ((ret = ptls_buffer_reserve(sendbuf, *ech_size_offset)) != 0)
- goto Exit;
- memset(sendbuf->base + sendbuf->off, 0, *ech_size_offset);
- sendbuf->off += *ech_size_offset;
- *ech_size_offset = sendbuf->off - *ech_size_offset;
+ ptls_buffer_push_block(sendbuf, 2, {
+ if ((ret = ptls_buffer_reserve(sendbuf, *ech_size_offset)) != 0)
+ goto Exit;
+ memset(sendbuf->base + sendbuf->off, 0, *ech_size_offset);
+ sendbuf->off += *ech_size_offset;
+ *ech_size_offset = sendbuf->off - *ech_size_offset;
+ });
});
} else {
- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO, { ptls_buffer_push(sendbuf, 1); });
+ buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO,
+ { ptls_buffer_push(sendbuf, PTLS_ECH_CLIENT_HELLO_TYPE_INNER); });
}
}
if (mode == ENCODE_CH_MODE_ENCODED_INNER) {
- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ECH_OUTER_EXTENSIONS,
- { ptls_buffer_push16(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE); });
+ buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ECH_OUTER_EXTENSIONS, {
+ ptls_buffer_push_block(sendbuf, 1, { ptls_buffer_push16(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE); });
+ });
} else {
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE, {
ptls_buffer_push_block(sendbuf, 2, {
@@ -3153,6 +3170,7 @@
static int decode_client_hello(ptls_context_t *ctx, struct st_ptls_client_hello_t *ch, const uint8_t *src, const uint8_t *const end,
ptls_handshake_properties_t *properties, ptls_t *tls_cbarg)
{
+ const uint8_t *start = src;
uint16_t exttype = 0;
int ret;
@@ -3209,6 +3227,8 @@
src = end;
});
+ ch->first_extension_at = src - start + 2;
+
/* decode extensions */
decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, &exttype, {
ch->psk.is_last_extension = 0;
@@ -3386,6 +3406,51 @@
case PTLS_EXTENSION_TYPE_STATUS_REQUEST:
ch->status_request = 1;
break;
+ case PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO: {
+ if (src == end) {
+ ret = PTLS_ALERT_DECODE_ERROR;
+ goto Exit;
+ }
+ ch->ech.type = *src++;
+ switch (ch->ech.type) {
+ case PTLS_ECH_CLIENT_HELLO_TYPE_OUTER:
+ if (src == end) {
+ ret = PTLS_ALERT_DECODE_ERROR;
+ goto Exit;
+ }
+ ch->ech.config_id = *src++;
+ ptls_hpke_cipher_suite_id_t cipher_id;
+ if ((ret = ptls_decode16(&cipher_id.kdf, &src, end)) != 0 || (ret = ptls_decode16(&cipher_id.aead, &src, end)) != 0)
+ goto Exit;
+ /* find corresponding cipher-suite; if not found, the field is left NULL */
+ for (size_t i = 0; ctx->ech.ciphers[i] != NULL; ++i) {
+ if (ctx->ech.ciphers[i]->id.kdf == cipher_id.kdf && ctx->ech.ciphers[i]->id.aead == cipher_id.aead) {
+ ch->ech.cipher = ctx->ech.ciphers[i];
+ break;
+ }
+ }
+ ptls_decode_open_block(src, end, 2, {
+ ch->ech.enc = ptls_iovec_init(src, end - src);
+ src = end;
+ });
+ ptls_decode_open_block(src, end, 2, {
+ ch->ech.payload = ptls_iovec_init(src, end - src);
+ src = end;
+ });
+ break;
+ case PTLS_ECH_CLIENT_HELLO_TYPE_INNER:
+ if (src != end) {
+ ret = PTLS_ALERT_DECODE_ERROR;
+ goto Exit;
+ }
+ ch->ech.payload = ptls_iovec_init("", 0); /* non-zero base indicates that the extension was received */
+ break;
+ default:
+ ret = PTLS_ALERT_ILLEGAL_PARAMETER;
+ goto Exit;
+ }
+ src = end;
+ } break;
default:
if (tls_cbarg != NULL && should_collect_unknown_extension(tls_cbarg, properties, exttype)) {
if ((ret = collect_unknown_extension(tls_cbarg, exttype, src, end, ch->unknown_extensions)) != 0)
@@ -3401,6 +3466,132 @@
return ret;
}
+static int rebuild_ch_inner(ptls_buffer_t *buf, const uint8_t *src, const uint8_t *const end,
+ struct st_ptls_client_hello_t *outer_ch, const uint8_t *outer_ext, const uint8_t *outer_ext_end)
+{
+#define COPY_BLOCK(capacity) \
+ do { \
+ ptls_decode_open_block(src, end, (capacity), { \
+ ptls_buffer_push_block(buf, (capacity), { ptls_buffer_pushv(buf, src, end - src); }); \
+ src = end; \
+ }); \
+ } while (0)
+
+ uint16_t exttype;
+ int ret;
+
+ ptls_buffer_push_message_body(buf, NULL, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, {
+ { /* legacy_version */
+ uint16_t legacy_version;
+ if ((ret = ptls_decode16(&legacy_version, &src, end)) != 0)
+ goto Exit;
+ ptls_buffer_push16(buf, legacy_version);
+ }
+
+ /* hello random */
+ if (end - src < PTLS_HELLO_RANDOM_SIZE) {
+ ret = PTLS_ALERT_DECODE_ERROR;
+ goto Exit;
+ }
+ ptls_buffer_pushv(buf, src, PTLS_HELLO_RANDOM_SIZE);
+ src += PTLS_HELLO_RANDOM_SIZE;
+
+ ptls_decode_open_block(src, end, 1, {
+ if (src != end) {
+ ret = PTLS_ALERT_ILLEGAL_PARAMETER;
+ goto Exit;
+ }
+ });
+ ptls_buffer_push_block(buf, 1,
+ { ptls_buffer_pushv(buf, outer_ch->legacy_session_id.base, outer_ch->legacy_session_id.len); });
+
+ /* cipher-suites and legacy-compression-methods */
+ COPY_BLOCK(2);
+ COPY_BLOCK(1);
+
+ /* extensions */
+ ptls_buffer_push_block(buf, 2, {
+ decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, &exttype, {
+ if (exttype == PTLS_EXTENSION_TYPE_ECH_OUTER_EXTENSIONS) {
+ ptls_decode_open_block(src, end, 1, {
+ do {
+ uint16_t reftype;
+ uint16_t outertype;
+ uint16_t outersize;
+ if ((ret = ptls_decode16(&reftype, &src, end)) != 0)
+ goto Exit;
+ while (1) {
+ if ((ret = ptls_decode16(&outertype, &outer_ext, outer_ext_end)) != 0 ||
+ (ret = ptls_decode16(&outersize, &outer_ext, outer_ext_end)) != 0)
+ goto Exit;
+ assert(outer_ext_end - outer_ext >= outersize);
+ if (outertype == reftype)
+ break;
+ outer_ext += outersize;
+ }
+ buffer_push_extension(buf, reftype, {
+ ptls_buffer_pushv(buf, outer_ext, outersize);
+ outer_ext += outersize;
+ });
+ } while (src != end);
+ });
+ } else {
+ buffer_push_extension(buf, exttype, {
+ ptls_buffer_pushv(buf, src, end - src);
+ src = end;
+ });
+ }
+ });
+ });
+ });
+
+Exit:
+ return ret;
+
+#undef COPY_BLOCK
+}
+
+static int check_client_hello_constraints(ptls_context_t *ctx, struct st_ptls_client_hello_t *ch, int is_second_flight,
+ ptls_iovec_t raw_message, ptls_t *tls_cbarg)
+{
+ /* bail out if CH cannot be handled as TLS 1.3, providing the application the raw CH and SNI, to help them fallback */
+ if (!is_supported_version(ch->selected_version)) {
+ if (!is_second_flight && ctx->on_client_hello != NULL) {
+ ptls_on_client_hello_parameters_t params = {
+ .server_name = ch->server_name,
+ .raw_message = raw_message,
+ .negotiated_protocols = {ch->alpn.list, ch->alpn.count},
+ .incompatible_version = 1,
+ };
+ int ret;
+ if ((ret = ctx->on_client_hello->cb(ctx->on_client_hello, tls_cbarg, ¶ms)) != 0)
+ return ret;
+ }
+ return PTLS_ALERT_PROTOCOL_VERSION;
+ }
+
+ /* Check TLS 1.3-specific constraints. Hereafter, we might exit without calling on_client_hello. That's fine because this CH is
+ * ought to be rejected. */
+ if (ch->legacy_version <= 0x0300) {
+ /* RFC 8446 Appendix D.5: any endpoint receiving a Hello message with legacy_version set to 0x0300 MUST abort the handshake
+ * with a "protocol_version" alert. */
+ return PTLS_ALERT_PROTOCOL_VERSION;
+ }
+ if (!(ch->compression_methods.count == 1 && ch->compression_methods.ids[0] == 0))
+ return PTLS_ALERT_ILLEGAL_PARAMETER;
+ /* pre-shared key */
+ if (ch->psk.hash_end != NULL) {
+ /* PSK must be the last extension */
+ if (!ch->psk.is_last_extension)
+ return PTLS_ALERT_ILLEGAL_PARAMETER;
+ } else {
+ if (ch->psk.early_data_indication)
+ return PTLS_ALERT_ILLEGAL_PARAMETER;
+ }
+
+ return 0;
+}
+
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';
@@ -3610,11 +3801,19 @@
ptls_key_exchange_algorithm_t *algorithm;
ptls_iovec_t peer_key;
} key_share = {NULL};
+ struct {
+ ptls_aead_context_t *aead;
+ uint8_t *encoded_ch_inner;
+ uint8_t *ch_outer_aad;
+ ptls_buffer_t ch_inner;
+ } ech = {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};
int accept_early_data = 0, is_second_flight = tls->state == PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO, ret;
+ ptls_buffer_init(&ech.ch_inner, "", 0);
+
if ((ch = malloc(sizeof(*ch))) == NULL) {
ret = PTLS_ERROR_NO_MEMORY;
goto Exit;
@@ -3626,46 +3825,43 @@
if ((ret = decode_client_hello(tls->ctx, ch, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len, properties,
tls)) != 0)
goto Exit;
+ if ((ret = check_client_hello_constraints(tls->ctx, ch, is_second_flight, message, tls)) != 0)
+ goto Exit;
- /* bail out if CH cannot be handled as TLS 1.3, providing the application the raw CH and SNI, to help them fallback */
- if (!is_supported_version(ch->selected_version)) {
- if (!is_second_flight && tls->ctx->on_client_hello != NULL) {
- ptls_on_client_hello_parameters_t params = {
- .server_name = ch->server_name,
- .raw_message = message,
- .negotiated_protocols = {ch->alpn.list, ch->alpn.count},
- .incompatible_version = 1,
- };
- if ((ret = tls->ctx->on_client_hello->cb(tls->ctx->on_client_hello, tls, ¶ms)) != 0)
+ /* ECH */
+ if (ch->ech.payload.base != NULL) {
+ if (ch->ech.type != PTLS_ECH_CLIENT_HELLO_TYPE_OUTER) {
+ ret = PTLS_ALERT_HANDSHAKE_FAILURE;
+ goto Exit;
+ }
+ /* obtain AEAD context for opening inner CH (FIXME second flight?) */
+ if (ch->ech.cipher != NULL && ch->ech.payload.len > ch->ech.cipher->aead->tag_size && tls->ctx->ech.create_opener != NULL &&
+ (ech.aead = tls->ctx->ech.create_opener->cb(tls->ctx->ech.create_opener, tls, ch->ech.config_id, ch->ech.cipher,
+ ch->ech.enc, ech_info_prefix)) != NULL) {
+ /* now that AEAD context is available, create AAD and decrypt inner CH */
+ if ((ech.encoded_ch_inner = malloc(ch->ech.payload.len - ech.aead->algo->tag_size)) == NULL ||
+ (ech.ch_outer_aad = malloc(message.len - PTLS_HANDSHAKE_HEADER_SIZE)) == NULL) {
+ ret = PTLS_ERROR_NO_MEMORY;
goto Exit;
- }
- ret = PTLS_ALERT_PROTOCOL_VERSION;
- goto Exit;
- }
-
- /* Check TLS 1.3-specific constraints. Hereafter, we might exit without calling on_client_hello. That's fine because this CH is
- * ought to be rejected. */
- if (ch->legacy_version <= 0x0300) {
- /* RFC 8446 Appendix D.5: any endpoint receiving a Hello message with legacy_version set to 0x0300 MUST abort the handshake
- * with a "protocol_version" alert. */
- ret = PTLS_ALERT_PROTOCOL_VERSION;
- goto Exit;
- }
- 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 (!ch->psk.is_last_extension) {
- ret = PTLS_ALERT_ILLEGAL_PARAMETER;
- goto Exit;
- }
- } else {
- if (ch->psk.early_data_indication) {
- ret = PTLS_ALERT_ILLEGAL_PARAMETER;
- goto Exit;
+ }
+ memcpy(ech.ch_outer_aad, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.len - PTLS_HANDSHAKE_HEADER_SIZE);
+ memset(ech.ch_outer_aad + (ch->ech.payload.base - (message.base + PTLS_HANDSHAKE_HEADER_SIZE)), 0, ch->ech.payload.len);
+ if (ptls_aead_decrypt(ech.aead, ech.encoded_ch_inner, ch->ech.payload.base, ch->ech.payload.len, 0, ech.ch_outer_aad,
+ message.len - PTLS_HANDSHAKE_HEADER_SIZE) != SIZE_MAX) {
+ /* successfully decrypted EncodedCHInner, build CHInner */
+ if ((ret = rebuild_ch_inner(
+ &ech.ch_inner, ech.encoded_ch_inner, ech.encoded_ch_inner + ch->ech.payload.len - ech.aead->algo->tag_size,
+ ch, message.base + PTLS_HANDSHAKE_HEADER_SIZE + ch->first_extension_at, message.base + message.len)) != 0)
+ goto Exit;
+ /* treat inner ch as the message being received, re-decode it */
+ message = ptls_iovec_init(ech.ch_inner.base, ech.ch_inner.off);
+ *ch = (struct st_ptls_client_hello_t){.unknown_extensions = {{UINT16_MAX}}};
+ if ((ret = decode_client_hello(tls->ctx, ch, ech.ch_inner.base + PTLS_HANDSHAKE_HEADER_SIZE,
+ ech.ch_inner.base + ech.ch_inner.off, properties, tls)) != 0)
+ goto Exit;
+ if ((ret = check_client_hello_constraints(tls->ctx, ch, is_second_flight, message, tls)) != 0)
+ goto Exit;
+ }
}
}
@@ -4047,6 +4243,11 @@
ptls_clear_memory(ecdh_secret.base, ecdh_secret.len);
free(ecdh_secret.base);
}
+ if (ech.aead != NULL)
+ ptls_aead_free(ech.aead);
+ free(ech.encoded_ch_inner);
+ free(ech.ch_outer_aad);
+ ptls_buffer_dispose(&ech.ch_inner);
free(ch);
return ret;
diff --git a/t/openssl.c b/t/openssl.c
index b9f7667..ffbc778 100644
--- a/t/openssl.c
+++ b/t/openssl.c
@@ -328,10 +328,36 @@
test_hpke(ptls_openssl_hpke_kems, ptls_openssl_hpke_cipher_suites);
}
+static ptls_aead_context_t *create_ech_opener(ptls_ech_create_opener_t *self, ptls_t *tls, uint8_t config_id,
+ ptls_hpke_cipher_suite_t *cipher, ptls_iovec_t enc, ptls_iovec_t info_prefix)
+{
+ static ptls_key_exchange_context_t *pem = NULL;
+ if (pem == NULL) {
+ pem = key_from_pem(ECH_PRIVATE_KEY);
+ assert(pem != NULL);
+ }
+
+ ptls_aead_context_t *aead = NULL;
+ ptls_buffer_t infobuf;
+ int ret;
+
+ ptls_buffer_init(&infobuf, "", 0);
+ ptls_buffer_pushv(&infobuf, info_prefix.base, info_prefix.len);
+ ptls_buffer_pushv(&infobuf, (const uint8_t *)ECH_CONFIG_LIST + 2,
+ sizeof(ECH_CONFIG_LIST) - 3); /* choose the only ECHConfig from the list */
+ ret = ptls_hpke_setup_base_r(&ptls_openssl_hpke_kem_p256sha256, cipher, pem, &aead, enc,
+ ptls_iovec_init(infobuf.base, infobuf.off));
+
+Exit:
+ ptls_buffer_dispose(&infobuf);
+ return aead;
+}
+
int main(int argc, char **argv)
{
ptls_openssl_sign_certificate_t openssl_sign_certificate;
ptls_openssl_verify_certificate_t openssl_verify_certificate;
+ ptls_ech_create_opener_t ech_create_opener = {.cb = create_ech_opener};
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
@@ -365,15 +391,13 @@
.cipher_suites = ptls_openssl_cipher_suites,
.tls12_cipher_suites = ptls_openssl_tls12_cipher_suites,
.certificates = {&cert, 1},
- .ech = {ptls_openssl_hpke_kems, ptls_openssl_hpke_cipher_suites},
+ .ech = {ptls_openssl_hpke_kems, ptls_openssl_hpke_cipher_suites, &ech_create_opener},
.sign_certificate = &openssl_sign_certificate.super};
assert(openssl_ctx.cipher_suites[0]->hash->digest_size == 48); /* sha384 */
ptls_context_t openssl_ctx_sha256only = openssl_ctx;
++openssl_ctx_sha256only.cipher_suites;
assert(openssl_ctx_sha256only.cipher_suites[0]->hash->digest_size == 32); /* sha256 */
- ptls_key_exchange_context_t *esni_private_keys[2] = {key_from_pem(ESNI_SECP256R1KEY), NULL};
-
ctx = ctx_peer = &openssl_ctx;
verify_certificate = &openssl_verify_certificate.super;
ADD_FFX_AES128_ALGORITHMS(openssl);
@@ -420,8 +444,6 @@
ctx_peer = &openssl_ctx;
subtest("minicrypto vs.", test_picotls);
- esni_private_keys[0]->on_exchange(esni_private_keys, 1, NULL, ptls_iovec_init(NULL, 0));
-
subtest("hpke", test_all_hpke);
int ret = done_testing();
diff --git a/t/picotls.c b/t/picotls.c
index 768b623..edb79b9 100644
--- a/t/picotls.c
+++ b/t/picotls.c
@@ -499,7 +499,7 @@
static void test_ech_decode_config(void)
{
- static ptls_hpke_kem_t x25519 = {PTLS_HPKE_KEM_X25519_SHA256}, *kems[] = {&x25519, NULL};
+ static ptls_hpke_kem_t p256 = {PTLS_HPKE_KEM_P256_SHA256}, *kems[] = {&p256, NULL};
static ptls_hpke_cipher_suite_t aes128gcmsha256 = {{PTLS_HPKE_HKDF_SHA256, PTLS_HPKE_AEAD_AES_128_GCM}},
*ciphers[] = {&aes128gcmsha256, NULL};
uint8_t config_id, max_name_length;
@@ -515,18 +515,15 @@
}
{
- uint8_t input[] = {0x12, 0x00, 0x20, 0x00, 0x20, 0x0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
- 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
- 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x08, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x40,
- 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00};
- const uint8_t *src = input, *const end = input + sizeof(input);
+ ptls_iovec_t input = ptls_iovec_init(ECH_CONFIG_LIST, sizeof(ECH_CONFIG_LIST) - 1);
+ const uint8_t *src = input.base + 6 /* dive into ECHConfigContents */, *const end = input.base + input.len;
int ret =
decode_one_ech_config(kems, ciphers, &config_id, &kem, &public_key, &cipher, &max_name_length, &public_name, &src, end);
ok(ret == 0);
ok(config_id == 0x12);
- ok(kem == &x25519);
- ok(public_key.len == 32);
- ok(public_key.base == input + 5);
+ ok(kem == &p256);
+ ok(public_key.len == 65);
+ ok(public_key.base == input.base + 11);
ok(cipher == &aes128gcmsha256);
ok(max_name_length == 64);
ok(public_name.len == sizeof("example.com") - 1);
@@ -700,14 +697,8 @@
ptls_set_server_name(client, "test.example.com", 0);
}
- if (ech) {
- static const uint8_t ech_config_list[] = {
- 0x00, 0x42, 0xfe, 0x0d, 0x00, 0x3e, 0x12, 0x00, 0x20, 0x00, 0x20, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
- 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
- 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x08, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01,
- 0x00, 0x01, 0x40, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00};
- client_hs_prop.client.ech_config_list = ptls_iovec_init(ech_config_list, sizeof(ech_config_list));
- }
+ if (ech)
+ client_hs_prop.client.ech_config_list = ptls_iovec_init(ECH_CONFIG_LIST, sizeof(ECH_CONFIG_LIST) - 1);
static ptls_on_extension_t cb = {on_extension_cb};
ctx_peer->on_extension = &cb;
diff --git a/t/test.h b/t/test.h
index 34a14c4..66667ef 100644
--- a/t/test.h
+++ b/t/test.h
@@ -51,18 +51,18 @@
"\x1d\x99\x42\xe0\xa2\xb7\x75\xbb\x14\x03\x79\x9a\xf6\x07\xd8\xa5\xab\x2b\x3a\x70\x8b\x77\x85\x70\x8a\x98\x38\x9b\x35\x09\xf6" \
"\x62\x6b\x29\x4a\xa7\xa7\xf9\x3b\xde\xd8\xc8\x90\x57\xf2\x76\x2a\x23\x0b\x01\x68\xc6\x9a\xf2"
-/* secp256r1 key that lasts until 2028 */
-#define ESNIKEYS \
- "\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/" \
- "2tjD6P9zDM20LEtVbqwBr20KYLG\n2e5mhKljlES+BOfuz6vCyd1A5siJiO2Uhg==\n-----END EC PRIVATE KEY-----\n"
+/* test vector from RFC 9180 A.3 */
+#define ECH_CONFIG_LIST \
+ "\x00\x63\xfe\x0d\x00\x5f\x12\x00\x10\x00\x41\x04\xfe\x8c\x19\xce\x09\x05\x19\x1e\xbc\x29\x8a\x92\x45\x79\x25\x31\xf2\x6f\x0c" \
+ "\xec\xe2\x46\x06\x39\xe8\xbc\x39\xcb\x7f\x70\x6a\x82\x6a\x77\x9b\x4c\xf9\x69\xb8\xa0\xe5\x39\xc7\xf6\x2f\xb3\xd3\x0a\xd6\xaa" \
+ "\x8f\x80\xe3\x0f\x1d\x12\x8a\xaf\xd6\x8a\x2c\xe7\x2e\xa0\x00\x08\x00\x02\x00\x02\x00\x01\x00\x01\x40\x0b\x65\x78\x61\x6d\x70" \
+ "\x6c\x65\x2e\x63\x6f\x6d\x00\x00"
+#define ECH_PRIVATE_KEY \
+ "-----BEGIN PRIVATE KEY-----\n" \
+ "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg885/2uV+GjENh/Hr\n" \
+ "vebzKL4Kmc28rfTWWJzyneS4/9KhRANCAAT+jBnOCQUZHrwpipJFeSUx8m8M7OJG\n" \
+ "BjnovDnLf3Bqgmp3m0z5abig5TnH9i+z0wrWqo+A4w8dEoqv1oos5y6g\n" \
+ "-----END PRIVATE KEY-----\n"
extern ptls_context_t *ctx, *ctx_peer;
extern ptls_verify_certificate_t *verify_certificate;