send ECH_REQUIRED alert after Finished, as the draft suggests
diff --git a/lib/picotls.c b/lib/picotls.c
index 47d8c26..976212f 100644
--- a/lib/picotls.c
+++ b/lib/picotls.c
@@ -3287,19 +3287,12 @@
static int client_handle_finished(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message)
{
uint8_t send_secret[PTLS_MAX_DIGEST_SIZE];
- int ret;
+ int alert_ech_required = tls->ech.offered && !ptls_is_ech_handshake(tls, NULL, NULL), ret;
if ((ret = verify_finished(tls, message)) != 0)
goto Exit;
ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len, 0);
- /* if ECH was rejected, close the connection with ECH_REQUIRED alert after verifying messages up to Finished (TODO send
- * ECH_REQUIRED alert after sending (an empty Certificate) and Finished message, as draft-15 suggests?) */
- if (tls->ech.offered && !ptls_is_ech_handshake(tls, NULL, NULL)) {
- ret = PTLS_ALERT_ECH_REQUIRED;
- goto Exit;
- }
-
/* 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;
@@ -3323,7 +3316,7 @@
if ((ret = push_change_cipher_spec(tls, emitter)) != 0)
goto Exit;
- if (tls->client.certificate_request.context.base != NULL) {
+ if (!alert_ech_required && tls->client.certificate_request.context.base != NULL) {
if ((ret = send_certificate(tls, emitter, &tls->client.certificate_request.signature_algorithms,
tls->client.certificate_request.context, 0, NULL, 0)) == 0)
ret = send_certificate_verify(tls, emitter, &tls->client.certificate_request.signature_algorithms,
@@ -3342,6 +3335,10 @@
tls->state = PTLS_STATE_CLIENT_POST_HANDSHAKE;
+ /* if ECH was rejected, close the connection with ECH_REQUIRED alert after verifying messages up to Finished */
+ if (alert_ech_required)
+ ret = PTLS_ALERT_ECH_REQUIRED;
+
Exit:
ptls_clear_memory(send_secret, sizeof(send_secret));
return ret;
@@ -5706,9 +5703,12 @@
case PTLS_ERROR_ASYNC_OPERATION:
break;
default:
- /* flush partially written response */
- ptls_clear_memory(emitter.super.buf->base + sendbuf_orig_off, emitter.super.buf->off - sendbuf_orig_off);
- emitter.super.buf->off = sendbuf_orig_off;
+ /* Flush handshake messages that have been written partially. ECH_REQUIRED sticks out because it is a message sent
+ * post-handshake compared to other alerts that are generating *during* the handshake. */
+ if (ret != PTLS_ALERT_ECH_REQUIRED) {
+ ptls_clear_memory(emitter.super.buf->base + sendbuf_orig_off, emitter.super.buf->off - sendbuf_orig_off);
+ emitter.super.buf->off = sendbuf_orig_off;
+ }
/* send alert immediately */
if (PTLS_ERROR_GET_CLASS(ret) != PTLS_ERROR_CLASS_PEER_ALERT)
if (ptls_send_alert(tls, emitter.super.buf, PTLS_ALERT_LEVEL_FATAL,