rfc-9849

TLS Encrypted Client Hello

E. Rescorla, K. Oku, N. Sullivan, C. A. Wood
date2026-03 streamIETF areasec wgtls statusPROPOSED STANDARD pages44 canonicalhttps://www.rfc-editor.org/rfc/rfc9849 doi10.17487/RFC9849
This document describes a mechanism in Transport Layer Security (TLS) for encrypting a message under a server public key.

Extracted elements (31)

design-rationale §10.10.5

ECH does not provide forward secrecy for ClientHelloInner because the server's ECH key is static (long-lived DNS key). The exposure window is bounded by the key lifetime, so servers are RECOMMENDED to rotate ECH keys regularly.

tls, crypto, security

design-rationale §3.1

ECH supports two topologies: Shared Mode (client-facing and backend server are the same entity) and Split Mode (client-facing server relays to a separate backend). In Split Mode, the client-facing server never sees the plaintext of the inner connection, and the spec assumes the inter-server channel is authenticated and correlation-resistant.

tls, privacy, security

design-rationale §10.10.4

GREASE ECH (Section 6.2) is included so that ECH-capable clients without an ECHConfig still send a fake ECH extension, providing 'cover' for real ECH connections and disincentivizing middleboxes from differentially blocking the extension. This addresses the RFC 8744 requirement to 'not stick out'.

tls, privacy, censorship

interoperability-note §8.1.2

TLS-terminating middleboxes that comply with RFC 8446 Section 9.3 will ignore the encrypted_client_hello extension and generate their own ClientHello for the plaintext server_name (the public name), preserving interoperability. A non-authoritative proxy cannot forge a signal to disable ECH.

tls, security

normative-requirement §6.1.6 MUST

After ECH rejection, if the server supplied valid retry_configs, the client MUST abort the current connection with an ech_required alert after handshake completion and SHOULD retry on a new transport connection using the retry configurations. The client MUST NOT fall back to unencrypted ClientHellos and MUST NOT use IP addresses from a new DNS query for the retry.

tls, security, privacy

normative-requirement §4.1 SHOULD

Client-facing servers SHOULD allocate distinct config_id values for each ECHConfig in their known set, using rejection sampling (randomly selecting until no collision with known values), to avoid performance penalties from trial decryption when multiple configs are active.

tls, security

normative-requirement §5.2 MUST

ClientHelloOuter is authenticated as HPKE associated data by constructing ClientHelloOuterAAD—a serialized ClientHello matching ClientHelloOuter but with the payload field of the encrypted_client_hello extension replaced by zeros—binding the outer to the inner and preventing attacker modification.

tls, security, crypto

normative-requirement §6.1.7 SHOULD

Clients SHOULD validate ECHConfig.contents.public_name before use and SHOULD ignore any ECHConfig whose public_name is not a valid dot-separated sequence of LDH labels or whose final label would be interpreted as an IPv4 literal (all-digit label or 0x/0X prefix).

tls, security, dns

normative-requirement §7.1 MUST

If all candidate ECHConfig values fail decryption, the client-facing server MUST proceed with ClientHelloOuter and, if configured with any ECHConfigs, MUST include the encrypted_client_hello extension in EncryptedExtensions with retry_configs set to one or more up-to-date ECHConfig structures.

tls, security

normative-requirement §7.1.1 MUST

If the client-facing server accepts ECH, when it receives the second ClientHelloOuter after HelloRetryRequest it MUST verify that cipher_suite and config_id are unchanged and that enc is empty; failure MUST result in an illegal_parameter abort. Decryption of the second payload reuses the original HPKE context.

tls, security

normative-requirement §6.2.1 SHOULD

If the client has no compatible ECHConfig, it SHOULD send a GREASE encrypted_client_hello extension with random config_id, a supported cipher suite, a randomly generated encapsulated key, and a random payload of L+C bytes matching the size of a real ECH payload with padding.

tls, privacy

normative-requirement §9 MUST

In the absence of an application profile specifying otherwise, a compliant ECH implementation MUST support the HPKE cipher suite: KEM DHKEM(X25519, HKDF-SHA256), KDF HKDF-SHA256, and AEAD AES-128-GCM.

tls, crypto

normative-requirement §5.1 MUST

The server MUST abort with an illegal_parameter alert if any padding byte in EncodedClientHelloInner is non-zero, if any referenced outer extension is missing, if any extension is referenced more than once in OuterExtensions, if encrypted_client_hello is itself referenced, or if the outer extensions are out of order.

tls, security

normative-requirement §6.1 MUST NOT

When constructing ClientHelloInner for ECH, the client MUST NOT offer TLS 1.2 or below, MUST NOT offer to resume any TLS 1.2-or-below session, MUST include the encrypted_client_hello extension of type inner, and MUST NOT choose a cipher suite or ECH version not advertised in the selected ECHConfig.

tls, security

normative-requirement §6.1.7 MUST

When ECH is rejected, the client MUST verify the server certificate is valid for ECHConfig.contents.public_name, interpreting it as a DNS-based reference identity per RFC 9525. If the server requests a client certificate, the client MUST respond with an empty Certificate message. The TLS implementation MUST NOT report the connection as successful to the application.

tls, security, pkix

normative-requirement §6.1.2 MUST NOT

When offering a PSK in ClientHelloInner, clients SHOULD send a GREASE pre_shared_key extension in ClientHelloOuter with randomized identities and binders of matching length to avoid revealing to observers that a PSK extension is present without solicitation. The client MUST NOT use this extension to advertise a real PSK to the client-facing server.

tls, privacy, security

privacy-consideration §10.3

A malicious client-facing server can distribute unique per-client ECHConfig values as tracking vectors. Clients can mitigate by flushing DNS and ECHConfig state when changing networks. ECHConfig rotation rate is also a trade-off: too-frequent rotation reduces the anonymity set size.

tls, privacy, security

protocol-element §4

ECH uses an ECHConfig structure to publish the server's HPKE public key and associated metadata. The structure includes a version (0xfe0d), a HpkeKeyConfig (config_id, kem_id, public_key, cipher_suites), maximum_name_length, public_name (the client-facing server's DNS name), and an extensions list.

tls, crypto, privacy

protocol-element §7.2.1

For HelloRetryRequest, the backend server signals ECH acceptance via an encrypted_client_hello extension carrying hrr_accept_confirmation = HKDF-Expand-Label(HKDF-Extract(0, ClientHelloInner1.random), "hrr ech accept confirmation", transcript_hrr_ech_conf, 8), computed over the HRR with the extension payload initially set to 8 zero bytes.

tls, crypto, security

protocol-element §7.2

The backend server signals ECH acceptance by computing accept_confirmation = HKDF-Expand-Label(HKDF-Extract(0, ClientHelloInner.random), "ech accept confirmation", transcript_ech_conf, 8) and overwriting the last 8 bytes of ServerHello.random with this value, where the transcript includes a ServerHello with those 8 bytes initially set to zero.

tls, crypto, security

protocol-element §5.1

The ech_outer_extensions extension (type 0xfd00) inside EncodedClientHelloInner allows the client to reference extensions already present in ClientHelloOuter rather than repeating them. The referenced extensions MUST appear contiguously and in the same relative order in ClientHelloOuter.

tls

protocol-element §6.1.3

The recommended padding scheme pads the SNI extension by max(0, M - D) bytes using ECHConfig.maximum_name_length M, or M+9 bytes if no SNI is present. After per-extension padding, the total EncodedClientHelloInner length is rounded up to the next multiple of 32 bytes to reduce length fingerprinting.

tls, privacy

registry §11.3

RFC 9849 creates the new ECH Configuration Extension registry (Section 11.3). Extension types with the high-order bit set are mandatory; clients MUST ignore ECHConfig structures containing unrecognized mandatory extensions. A set of Reserved codepoints is defined for server GREASE use and will never be assigned.

tls, registry

registry §11.1

RFC 9849 registers the encrypted_client_hello extension (type 0xfe0d) and the ech_outer_extensions extension (type 0xfd00) in the IANA TLS ExtensionType Values registry, and registers the ech_required alert in the TLS Alert Registry.

tls, registry

security-consideration §10.2

ECH configurations delivered via unauthenticated DNS can be stripped or replaced by an attacker who controls DNS, but this does not worsen the situation significantly since such an attacker can already reroute connections. DNSSEC and encrypted DNS transports (DoH, DoT, DoQ) are recommended mitigations.

tls, security, dns, privacy

security-consideration §10.1

ECH's three primary security goals are: (1) security preservation—ECH does not weaken TLS; (2) handshake privacy—connections to any server name in an anonymity set are indistinguishable from connections to any other name in that set; and (3) downgrade resistance—attackers cannot force a non-ECH connection. These properties were formally proven in the ECH-Analysis paper.

tls, security, privacy

security-consideration §5.1

To prevent a packet amplification attack, the server enforces that ech_outer_extensions references must appear contiguously, not be duplicated, and not reference encrypted_client_hello itself. Additionally, server implementations SHOULD construct ClientHelloInner in linear time to avoid quadratic denial-of-service from naive extension expansion.

tls, security

state-machine §6.1.4

ECH acceptance is determined from the server's first response: if TLS < 1.3 is negotiated, ECH is rejected. If ServerHello, the client checks accept_confirmation against the last 8 bytes of ServerHello.random. If HelloRetryRequest, the client checks for an encrypted_client_hello extension; an 8-byte match against hrr_accept_confirmation indicates acceptance; absent extension means rejection.

tls, security

wire-format §5.1

EncodedClientHelloInner wraps ClientHelloInner with a zeros padding field: struct { ClientHello client_hello; uint8 zeros[length_of_padding]; }. The legacy_session_id field is set to the empty string before encryption, and all padding bytes MUST be zero.

tls, privacy

wire-format §4

The ECHConfigList is a length-prefixed sequence of ECHConfig structures (ECHConfig ECHConfigList<4..2^16-1>), ordered from most to least preferred, allowing servers to advertise multiple ECH versions and parameter sets simultaneously.

tls, crypto

wire-format §5

The encrypted_client_hello extension (type 0xfe0d) carries an ECHClientHello with an outer or inner type. The outer variant includes cipher_suite, config_id (1 byte), enc (HPKE encapsulated key, empty on HRR), and payload (encrypted EncodedClientHelloInner). The inner variant has an empty payload.

tls, crypto