| rfc9849v3.md | rfc9849.md | |||
|---|---|---|---|---|
| --- | --- | |||
| title: TLS Encrypted Client Hello | title: TLS Encrypted Client Hello | |||
| abbrev: TLS Encrypted Client Hello | abbrev: TLS Encrypted Client Hello | |||
| docname: draft-ietf-tls-esni-25 | docname: draft-ietf-tls-esni-25 | |||
| category: std | category: std | |||
| number: 9849 | number: 9849 | |||
| ipr: trust200902 | ipr: trust200902 | |||
| submissiontype: IETF | submissiontype: IETF | |||
| updates: | updates: | |||
| obsoletes: | obsoletes: | |||
| date: 2025-12 | date: 2026-02 | |||
| consensus: true | consensus: true | |||
| v: 3 | v: 3 | |||
| area: SEC | area: SEC | |||
| workgroup: tls | workgroup: tls | |||
| keyword: | keyword: | |||
| stand_alone: yes | stand_alone: yes | |||
| pi: [toc, sortrefs, symrefs] | pi: [toc, sortrefs, symrefs] | |||
| author: | author: | |||
| - name: Eric Rescorla | - name: Eric Rescorla | |||
| ins: E. Rescorla | ins: E. Rescorla | |||
| org: Knight-Georgetown Institute | org: Independent | |||
| email: ekr@rtfm.com | email: ekr@rtfm.com | |||
| - name: Kazuho Oku | - name: Kazuho Oku | |||
| ins: K. Oku | ins: K. Oku | |||
| org: Fastly | org: Fastly | |||
| email: kazuhooku@gmail.com | email: kazuhooku@gmail.com | |||
| - name: Nick Sullivan | - name: Nick Sullivan | |||
| ins: N. Sullivan | ins: N. Sullivan | |||
| org: Cryptography Consulting LLC | org: Cryptography Consulting LLC | |||
| email: nicholas.sullivan+ietf@gmail.com | email: nicholas.sullivan+ietf@gmail.com | |||
| - name: Christopher A. Wood | - name: Christopher A. Wood | |||
| ins: C. A. Wood | ins: C. A. Wood | |||
| org: Cloudflare | org: Apple | |||
| email: caw@heapingbits.net | email: caw@heapingbits.net | |||
| normative: | normative: | |||
| RFC2119: | RFC2119: | |||
| RFC7918: | RFC7918: | |||
| RFC9180: | RFC9180: | |||
| display: HPKE | display: HPKE | |||
| informative: | informative: | |||
| skipping to change at line 198 ¶ | skipping to change at line 198 ¶ | |||
| notation comes from {{RFC8446, Section 3}}. | notation comes from {{RFC8446, Section 3}}. | |||
| # Overview | # Overview | |||
| This protocol is designed to operate in one of two topologies illustrated below, | This protocol is designed to operate in one of two topologies illustrated below, | |||
| which we call "Shared Mode" and "Split Mode". These modes are described in the | which we call "Shared Mode" and "Split Mode". These modes are described in the | |||
| following section. | following section. | |||
| ## Topologies | ## Topologies | |||
| ~~~~ | ~~~ | |||
| +---------------------+ | +---------------------+ | |||
| | | | | | | |||
| | 2001:DB8::1111 | | | 2001:DB8::1111 | | |||
| | | | | | | |||
| Client <-----> | private.example.org | | Client <-----> | private.example.org | | |||
| | | | | | | |||
| | public.example.com | | | public.example.com | | |||
| | | | | | | |||
| +---------------------+ | +---------------------+ | |||
| Server | Server | |||
| (Client-Facing and Backend Combined) | (Client-Facing and Backend Combined) | |||
| ~~~~ | ~~~ | |||
| {: #shared-mode title="Shared Mode Topology"} | {: #shared-mode title="Shared Mode Topology"} | |||
| In shared mode, the provider is the origin server for all the domains whose DNS | In shared mode, the provider is the origin server for all the domains whose DNS | |||
| records point to it. In this mode, the TLS connection is terminated by the | records point to it. In this mode, the TLS connection is terminated by the | |||
| provider. | provider. | |||
| ~~~~ | ~~~ | |||
| +--------------------+ +---------------------+ | +--------------------+ +---------------------+ | |||
| | | | | | | | | | | |||
| | 2001:DB8::1111 | | 2001:DB8::EEEE | | | 2001:DB8::1111 | | 2001:DB8::EEEE | | |||
| Client <----------------------------->| | | Client <----------------------------->| | | |||
| | public.example.com | | private.example.org | | | public.example.com | | private.example.org | | |||
| | | | | | | | | | | |||
| +--------------------+ +---------------------+ | +--------------------+ +---------------------+ | |||
| Client-Facing Server Backend Server | Client-Facing Server Backend Server | |||
| ~~~~ | ~~~ | |||
| {: #split-mode title="Split Mode Topology"} | {: #split-mode title="Split Mode Topology"} | |||
| In split mode, the provider is not the origin server for private domains. | In split mode, the provider is not the origin server for private domains. | |||
| Rather, the DNS records for private domains point to the provider, and the | Rather, the DNS records for private domains point to the provider, and the | |||
| provider's server relays the connection back to the origin server, who | provider's server relays the connection back to the origin server, who | |||
| terminates the TLS connection with the client. Importantly, the service provider | terminates the TLS connection with the client. Importantly, the service provider | |||
| does not have access to the plaintext of the connection beyond the unencrypted | does not have access to the plaintext of the connection beyond the unencrypted | |||
| portions of the handshake. | portions of the handshake. | |||
| In the remainder of this document, we will refer to the ECH-service provider as | In the remainder of this document, we will refer to the ECH-service provider as | |||
| skipping to change at line 289 ¶ | skipping to change at line 289 ¶ | |||
| The primary goal of ECH is to ensure that connections to servers in the same | The primary goal of ECH is to ensure that connections to servers in the same | |||
| anonymity set are indistinguishable from one another. Moreover, it should | anonymity set are indistinguishable from one another. Moreover, it should | |||
| achieve this goal without affecting any existing security properties of TLS 1.3. | achieve this goal without affecting any existing security properties of TLS 1.3. | |||
| See {{goals}} for more details about the ECH security and privacy goals. | See {{goals}} for more details about the ECH security and privacy goals. | |||
| # Encrypted ClientHello Configuration {#ech-configuration} | # Encrypted ClientHello Configuration {#ech-configuration} | |||
| ECH uses Hybrid Public Key Encryption (HPKE) for public key encryption {{RFC9180}}. | ECH uses Hybrid Public Key Encryption (HPKE) for public key encryption {{RFC9180}}. | |||
| The ECH configuration is defined by the following `ECHConfig` structure. | The ECH configuration is defined by the following `ECHConfig` structure. | |||
| ~~~~ | ~~~ | |||
| opaque HpkePublicKey<1..2^16-1>; | opaque HpkePublicKey<1..2^16-1>; | |||
| uint16 HpkeKemId; // Defined in RFC 9180 | uint16 HpkeKemId; // Defined in RFC 9180 | |||
| uint16 HpkeKdfId; // Defined in RFC 9180 | uint16 HpkeKdfId; // Defined in RFC 9180 | |||
| uint16 HpkeAeadId; // Defined in RFC 9180 | uint16 HpkeAeadId; // Defined in RFC 9180 | |||
| uint16 ECHConfigExtensionType; // Defined in Section 11.3 | uint16 ECHConfigExtensionType; // Defined in Section 11.3 | |||
| struct { | struct { | |||
| HpkeKdfId kdf_id; | HpkeKdfId kdf_id; | |||
| HpkeAeadId aead_id; | HpkeAeadId aead_id; | |||
| } HpkeSymmetricCipherSuite; | } HpkeSymmetricCipherSuite; | |||
| skipping to change at line 327 ¶ | skipping to change at line 327 ¶ | |||
| ECHConfigExtension extensions<0..2^16-1>; | ECHConfigExtension extensions<0..2^16-1>; | |||
| } ECHConfigContents; | } ECHConfigContents; | |||
| struct { | struct { | |||
| uint16 version; | uint16 version; | |||
| uint16 length; | uint16 length; | |||
| select (ECHConfig.version) { | select (ECHConfig.version) { | |||
| case 0xfe0d: ECHConfigContents contents; | case 0xfe0d: ECHConfigContents contents; | |||
| } | } | |||
| } ECHConfig; | } ECHConfig; | |||
| ~~~~ | ~~~ | |||
| The structure contains the following fields: | The structure contains the following fields: | |||
| version: | version: | |||
| : The version of ECH for which this configuration is used. The version | : The version of ECH for which this configuration is used. The version | |||
| is the same as the code point for the | is the same as the code point for the | |||
| "encrypted_client_hello" extension. Clients MUST ignore any `ECHConfig` | "encrypted_client_hello" extension. Clients MUST ignore any `ECHConfig` | |||
| structure with a version they do not support. | structure with a version they do not support. | |||
| length: | length: | |||
| skipping to change at line 397 ¶ | skipping to change at line 397 ¶ | |||
| `public_key`: | `public_key`: | |||
| : The HPKE public key used by the client to encrypt `ClientHelloInner`. | : The HPKE public key used by the client to encrypt `ClientHelloInner`. | |||
| cipher_suites: | cipher_suites: | |||
| : The list of HPKE Key Derivation Function (KDF) and Authenticated Encryption with As sociated Data (AEAD) identifier pairs clients can use for encrypting | : The list of HPKE Key Derivation Function (KDF) and Authenticated Encryption with As sociated Data (AEAD) identifier pairs clients can use for encrypting | |||
| `ClientHelloInner`. See {{real-ech}} for how clients choose from this list. | `ClientHelloInner`. See {{real-ech}} for how clients choose from this list. | |||
| The client-facing server advertises a sequence of ECH configurations to clients, | The client-facing server advertises a sequence of ECH configurations to clients, | |||
| serialized as follows. | serialized as follows. | |||
| ~~~~ | ~~~ | |||
| ECHConfig ECHConfigList<4..2^16-1>; | ECHConfig ECHConfigList<4..2^16-1>; | |||
| ~~~~ | ~~~ | |||
| The `ECHConfigList` structure contains one or more `ECHConfig` structures in | The `ECHConfigList` structure contains one or more `ECHConfig` structures in | |||
| decreasing order of preference. This allows a server to support multiple | decreasing order of preference. This allows a server to support multiple | |||
| versions of ECH and multiple sets of ECH parameters. | versions of ECH and multiple sets of ECH parameters. | |||
| ## Configuration Identifiers {#config-ids} | ## Configuration Identifiers {#config-ids} | |||
| A client-facing server has a set of known `ECHConfig` values with corresponding | A client-facing server has a set of known `ECHConfig` values with corresponding | |||
| private keys. This set SHOULD contain the currently published values, as well as | private keys. This set SHOULD contain the currently published values, as well as | |||
| previous values that may still be in use, since clients may cache DNS records | previous values that may still be in use, since clients may cache DNS records | |||
| skipping to change at line 455 ¶ | skipping to change at line 455 ¶ | |||
| the encrypted inner `ClientHello` and an enabler for authenticated key mismatch | the encrypted inner `ClientHello` and an enabler for authenticated key mismatch | |||
| signals (see {{server-behavior}}). In contrast, the inner `ClientHello` is the | signals (see {{server-behavior}}). In contrast, the inner `ClientHello` is the | |||
| true `ClientHello` used upon ECH negotiation. | true `ClientHello` used upon ECH negotiation. | |||
| # The "encrypted_client_hello" Extension {#encrypted-client-hello} | # The "encrypted_client_hello" Extension {#encrypted-client-hello} | |||
| To offer ECH, the client sends an "encrypted_client_hello" extension in the | To offer ECH, the client sends an "encrypted_client_hello" extension in the | |||
| `ClientHelloOuter`. When it does, it MUST also send the extension in | `ClientHelloOuter`. When it does, it MUST also send the extension in | |||
| `ClientHelloInner`. | `ClientHelloInner`. | |||
| ~~~ | ~~ | |||
| enum { | enum { | |||
| encrypted_client_hello(0xfe0d), (65535) | encrypted_client_hello(0xfe0d), (65535) | |||
| } ExtensionType; | } ExtensionType; | |||
| ~~~ | ~~ | |||
| The payload of the extension has the following structure: | The payload of the extension has the following structure: | |||
| ~~~~ | ~~~ | |||
| enum { outer(0), inner(1) } ECHClientHelloType; | enum { outer(0), inner(1) } ECHClientHelloType; | |||
| struct { | struct { | |||
| ECHClientHelloType type; | ECHClientHelloType type; | |||
| select (ECHClientHello.type) { | select (ECHClientHello.type) { | |||
| case outer: | case outer: | |||
| HpkeSymmetricCipherSuite cipher_suite; | HpkeSymmetricCipherSuite cipher_suite; | |||
| uint8 config_id; | uint8 config_id; | |||
| opaque enc<0..2^16-1>; | opaque enc<0..2^16-1>; | |||
| opaque payload<1..2^16-1>; | opaque payload<1..2^16-1>; | |||
| case inner: | case inner: | |||
| Empty; | Empty; | |||
| }; | }; | |||
| } ECHClientHello; | } ECHClientHello; | |||
| ~~~~ | ~~~ | |||
| The outer extension uses the `outer` variant and the inner extension uses the | The outer extension uses the `outer` variant and the inner extension uses the | |||
| `inner` variant. The inner extension has an empty payload, which is included | `inner` variant. The inner extension has an empty payload, which is included | |||
| because TLS servers are not allowed to provide extensions in ServerHello | because TLS servers are not allowed to provide extensions in ServerHello | |||
| which were not included in `ClientHello`. The outer extension has the following | which were not included in `ClientHello`. The outer extension has the following | |||
| fields: | fields: | |||
| `config_id`: | `config_id`: | |||
| : The `ECHConfigContents.key_config.config_id` for the chosen `ECHConfig`. | : The `ECHConfigContents.key_config.config_id` for the chosen `ECHConfig`. | |||
| skipping to change at line 507 ¶ | skipping to change at line 507 ¶ | |||
| payload: | payload: | |||
| : The serialized and encrypted `EncodedClientHelloInner` structure, encrypted | : The serialized and encrypted `EncodedClientHelloInner` structure, encrypted | |||
| using HPKE as described in {{real-ech}}. | using HPKE as described in {{real-ech}}. | |||
| When a client offers the `outer` version of an "encrypted_client_hello" | When a client offers the `outer` version of an "encrypted_client_hello" | |||
| extension, the server MAY include an "encrypted_client_hello" extension in its | extension, the server MAY include an "encrypted_client_hello" extension in its | |||
| EncryptedExtensions message, as described in {{client-facing-server}}, with the | EncryptedExtensions message, as described in {{client-facing-server}}, with the | |||
| following payload: | following payload: | |||
| ~~~ | ~~ | |||
| struct { | struct { | |||
| ECHConfigList retry_configs; | ECHConfigList retry_configs; | |||
| } ECHEncryptedExtensions; | } ECHEncryptedExtensions; | |||
| ~~~ | ~~ | |||
| The response is valid only when the server used the `ClientHelloOuter`. If the | The response is valid only when the server used the `ClientHelloOuter`. If the | |||
| server sent this extension in response to the `inner` variant, then the client | server sent this extension in response to the `inner` variant, then the client | |||
| MUST abort with an "unsupported_extension" alert. | MUST abort with an "unsupported_extension" alert. | |||
| retry_configs: | retry_configs: | |||
| : An `ECHConfigList` structure containing one or more `ECHConfig` structures, in | : An `ECHConfigList` structure containing one or more `ECHConfig` structures, in | |||
| decreasing order of preference, to be used by the client as described in | decreasing order of preference, to be used by the client as described in | |||
| {{rejected-ech}}. These are known as the server's "retry configurations". | {{rejected-ech}}. These are known as the server's "retry configurations". | |||
| Finally, when the client offers the "encrypted_client_hello", if the payload is | Finally, when the client offers the "encrypted_client_hello", if the payload is | |||
| the `inner` variant and the server responds with HelloRetryRequest, it MUST | the `inner` variant and the server responds with HelloRetryRequest, it MUST | |||
| include an "encrypted_client_hello" extension with the following payload: | include an "encrypted_client_hello" extension with the following payload: | |||
| ~~~ | ~~ | |||
| struct { | struct { | |||
| opaque confirmation[8]; | opaque confirmation[8]; | |||
| } ECHHelloRetryRequest; | } ECHHelloRetryRequest; | |||
| ~~~ | ~~ | |||
| The value of ECHHelloRetryRequest.confirmation is set to | The value of ECHHelloRetryRequest.confirmation is set to | |||
| `hrr_accept_confirmation` as described in {{backend-server-hrr}}. | `hrr_accept_confirmation` as described in {{backend-server-hrr}}. | |||
| This document also defines the "ech_required" alert, which the client MUST send | This document also defines the "ech_required" alert, which the client MUST send | |||
| when it offered an "encrypted_client_hello" extension that was not accepted by | when it offered an "encrypted_client_hello" extension that was not accepted by | |||
| the server. (See {{alerts}}.) | the server. (See {{alerts}}.) | |||
| ## Encoding the ClientHelloInner {#encoding-inner} | ## Encoding the ClientHelloInner {#encoding-inner} | |||
| Before encrypting, the client pads and optionally compresses `ClientHelloInner` | Before encrypting, the client pads and optionally compresses `ClientHelloInner` | |||
| into an `EncodedClientHelloInner` structure, defined below: | into an `EncodedClientHelloInner` structure, defined below: | |||
| ~~~ | ~~ | |||
| struct { | struct { | |||
| ClientHello client_hello; | ClientHello client_hello; | |||
| uint8 zeros[length_of_padding]; | uint8 zeros[length_of_padding]; | |||
| } EncodedClientHelloInner; | } EncodedClientHelloInner; | |||
| ~~~ | ~~ | |||
| The `client_hello` field is computed by first making a copy of `ClientHelloInner` | The `client_hello` field is computed by first making a copy of `ClientHelloInner` | |||
| and setting the `legacy_session_id` field to the empty string. In TLS, this | and setting the `legacy_session_id` field to the empty string. In TLS, this | |||
| field uses the `ClientHello` structure defined in {{Section 4.1.2 of RFC8446}}. | field uses the `ClientHello` structure defined in {{Section 4.1.2 of RFC8446}}. | |||
| In DTLS, it uses the `ClientHello` structure defined in | In DTLS, it uses the `ClientHello` structure defined in | |||
| {{Section 5.3 of RFC9147}}. This does not include Handshake structure's | {{Section 5.3 of RFC9147}}. This does not include Handshake structure's | |||
| four-byte header in TLS, nor twelve-byte header in DTLS. The `zeros` field MUST | four-byte header in TLS, nor twelve-byte header in DTLS. The `zeros` field MUST | |||
| be all zeroes of length `length_of_padding` (see {{padding}}). | be all zeroes of length `length_of_padding` (see {{padding}}). | |||
| Repeating large extensions, such as "key_share" with post-quantum algorithms, | Repeating large extensions, such as "key_share" with post-quantum algorithms, | |||
| between `ClientHelloInner` and `ClientHelloOuter` can lead to excessive size. To | between `ClientHelloInner` and `ClientHelloOuter` can lead to excessive size. To | |||
| reduce the size impact, the client MAY substitute extensions which it knows | reduce the size impact, the client MAY substitute extensions which it knows | |||
| will be duplicated in `ClientHelloOuter`. It does so by removing and replacing | will be duplicated in `ClientHelloOuter`. It does so by removing and replacing | |||
| extensions from `EncodedClientHelloInner` with a single "ech_outer_extensions" | extensions from `EncodedClientHelloInner` with a single "ech_outer_extensions" | |||
| extension, defined as follows: | extension, defined as follows: | |||
| ~~~ | ~~ | |||
| enum { | enum { | |||
| ech_outer_extensions(0xfd00), (65535) | ech_outer_extensions(0xfd00), (65535) | |||
| } ExtensionType; | } ExtensionType; | |||
| ExtensionType OuterExtensions<2..254>; | ExtensionType OuterExtensions<2..254>; | |||
| ~~~ | ~~ | |||
| OuterExtensions contains the removed ExtensionType values. Each value references | OuterExtensions contains the removed ExtensionType values. Each value references | |||
| the matching extension in `ClientHelloOuter`. The values MUST be ordered | the matching extension in `ClientHelloOuter`. The values MUST be ordered | |||
| contiguously in `ClientHelloInner`, and the "ech_outer_extensions" extension MUST | contiguously in `ClientHelloInner`, and the "ech_outer_extensions" extension MUST | |||
| be inserted in the corresponding position in `EncodedClientHelloInner`. | be inserted in the corresponding position in `EncodedClientHelloInner`. | |||
| Additionally, the extensions MUST appear in `ClientHelloOuter` in the same | Additionally, the extensions MUST appear in `ClientHelloOuter` in the same | |||
| relative order. However, there is no requirement that they be contiguous. For | relative order. However, there is no requirement that they be contiguous. For | |||
| example, OuterExtensions may contain extensions A, B, and C, while `ClientHelloOuter` | example, OuterExtensions may contain extensions A, B, and C, while `ClientHelloOuter` | |||
| contains extensions A, D, B, C, E, and F. | contains extensions A, D, B, C, E, and F. | |||
| skipping to change at line 653 ¶ | skipping to change at line 653 ¶ | |||
| the server. (See {{dont-stick-out}} for an explanation.) The client offers ECH | the server. (See {{dont-stick-out}} for an explanation.) The client offers ECH | |||
| if it is in possession of a compatible ECH configuration and sends GREASE ECH | if it is in possession of a compatible ECH configuration and sends GREASE ECH | |||
| (see {{grease-ech}}) otherwise. | (see {{grease-ech}}) otherwise. | |||
| ## Offering ECH {#real-ech} | ## Offering ECH {#real-ech} | |||
| To offer ECH, the client first chooses a suitable `ECHConfig` from the server's | To offer ECH, the client first chooses a suitable `ECHConfig` from the server's | |||
| `ECHConfigList`. To determine if a given `ECHConfig` is suitable, it checks that | `ECHConfigList`. To determine if a given `ECHConfig` is suitable, it checks that | |||
| it supports the KEM algorithm identified by `ECHConfig.contents.kem_id`, at | it supports the KEM algorithm identified by `ECHConfig.contents.kem_id`, at | |||
| least one KDF/AEAD algorithm identified by `ECHConfig.contents.cipher_suites`, | least one KDF/AEAD algorithm identified by `ECHConfig.contents.cipher_suites`, | |||
| and the version of ECH indicated by `ECHConfig.contents.version`. Once a | and the version of ECH indicated by `ECHConfig.version`. Once a | |||
| suitable configuration is found, the client selects the cipher suite it will | suitable configuration is found, the client selects the cipher suite it will | |||
| use for encryption. It MUST NOT choose a cipher suite or version not advertised | use for encryption. It MUST NOT choose a cipher suite or version not advertised | |||
| by the configuration. If no compatible configuration is found, then the client | by the configuration. If no compatible configuration is found, then the client | |||
| SHOULD proceed as described in {{grease-ech}}. | SHOULD proceed as described in {{grease-ech}}. | |||
| Next, the client constructs the `ClientHelloInner` message just as it does a | Next, the client constructs the `ClientHelloInner` message just as it does a | |||
| standard `ClientHello`, with the exception of the following rules: | standard `ClientHello`, with the exception of the following rules: | |||
| 1. It MUST NOT offer to negotiate TLS 1.2 or below. This is necessary to ensure | 1. It MUST NOT offer to negotiate TLS 1.2 or below. This is necessary to ensure | |||
| the backend server does not negotiate a TLS version that is incompatible with | the backend server does not negotiate a TLS version that is incompatible with | |||
| skipping to change at line 677 ¶ | skipping to change at line 677 ¶ | |||
| order those extensions consecutively. | order those extensions consecutively. | |||
| 1. It MUST include the "encrypted_client_hello" extension of type `inner` as | 1. It MUST include the "encrypted_client_hello" extension of type `inner` as | |||
| described in {{encrypted-client-hello}}. (This requirement is not applicable | described in {{encrypted-client-hello}}. (This requirement is not applicable | |||
| when the "encrypted_client_hello" extension is generated as described in | when the "encrypted_client_hello" extension is generated as described in | |||
| {{grease-ech}}.) | {{grease-ech}}.) | |||
| The client then constructs `EncodedClientHelloInner` as described in | The client then constructs `EncodedClientHelloInner` as described in | |||
| {{encoding-inner}}. It also computes an HPKE encryption context and `enc` value | {{encoding-inner}}. It also computes an HPKE encryption context and `enc` value | |||
| as: | as: | |||
| ~~~ | ~~ | |||
| pkR = DeserializePublicKey(ECHConfig.contents.public_key) | pkR = DeserializePublicKey(ECHConfig.contents.public_key) | |||
| enc, context = SetupBaseS(pkR, | enc, context = SetupBaseS(pkR, | |||
| "tls ech" || 0x00 || ECHConfig) | "tls ech" || 0x00 || ECHConfig) | |||
| ~~~ | ~~ | |||
| Next, it constructs a partial `ClientHelloOuterAAD` as it does a standard | Next, it constructs a partial `ClientHelloOuterAAD` as it does a standard | |||
| `ClientHello`, with the exception of the following rules: | `ClientHello`, with the exception of the following rules: | |||
| 1. It MUST offer to negotiate TLS 1.3 or above. | 1. It MUST offer to negotiate TLS 1.3 or above. | |||
| 1. If it compressed any extensions in `EncodedClientHelloInner`, it MUST copy the | 1. If it compressed any extensions in `EncodedClientHelloInner`, it MUST copy the | |||
| corresponding extensions from `ClientHelloInner`. The copied extensions | corresponding extensions from `ClientHelloInner`. The copied extensions | |||
| additionally MUST be in the same relative order as in `ClientHelloInner`. | additionally MUST be in the same relative order as in `ClientHelloInner`. | |||
| 1. It MUST copy the legacy\_session\_id field from `ClientHelloInner`. This | 1. It MUST copy the legacy\_session\_id field from `ClientHelloInner`. This | |||
| allows the server to echo the correct session ID for TLS 1.3's compatibility | allows the server to echo the correct session ID for TLS 1.3's compatibility | |||
| skipping to change at line 751 ¶ | skipping to change at line 751 ¶ | |||
| - `payload`, a placeholder byte string containing L zeros. | - `payload`, a placeholder byte string containing L zeros. | |||
| If configuration identifiers (see {{ignored-configs}}) are to be | If configuration identifiers (see {{ignored-configs}}) are to be | |||
| ignored, `config_id` SHOULD be set to a randomly generated byte in the | ignored, `config_id` SHOULD be set to a randomly generated byte in the | |||
| first `ClientHelloOuter` and, in the event of a HelloRetryRequest (HRR), | first `ClientHelloOuter` and, in the event of a HelloRetryRequest (HRR), | |||
| MUST be left unchanged for the second `ClientHelloOuter`. | MUST be left unchanged for the second `ClientHelloOuter`. | |||
| The client serializes this structure to construct the `ClientHelloOuterAAD`. | The client serializes this structure to construct the `ClientHelloOuterAAD`. | |||
| It then computes the final payload as: | It then computes the final payload as: | |||
| ~~~ | ~~ | |||
| final_payload = context.Seal(ClientHelloOuterAAD, | final_payload = context.Seal(ClientHelloOuterAAD, | |||
| EncodedClientHelloInner) | EncodedClientHelloInner) | |||
| ~~~ | ~~ | |||
| Including `ClientHelloOuterAAD` as the HPKE AAD binds the `ClientHelloOuter` | Including `ClientHelloOuterAAD` as the HPKE AAD binds the `ClientHelloOuter` | |||
| to the `ClientHelloInner`, thus preventing attackers from modifying | to the `ClientHelloInner`, thus preventing attackers from modifying | |||
| `ClientHelloOuter` while keeping the same `ClientHelloInner`, as described in | `ClientHelloOuter` while keeping the same `ClientHelloInner`, as described in | |||
| {{flow-clienthello-malleability}}. | {{flow-clienthello-malleability}}. | |||
| Finally, the client replaces `payload` with `final_payload` to obtain | Finally, the client replaces `payload` with `final_payload` to obtain | |||
| `ClientHelloOuter`. The two values have the same length, so it is not necessary | `ClientHelloOuter`. The two values have the same length, so it is not necessary | |||
| to recompute length prefixes in the serialized structure. | to recompute length prefixes in the serialized structure. | |||
| skipping to change at line 816 ¶ | skipping to change at line 816 ¶ | |||
| By way of example, clients typically support a small number of application | By way of example, clients typically support a small number of application | |||
| profiles. For instance, a browser might support HTTP with ALPN values | profiles. For instance, a browser might support HTTP with ALPN values | |||
| ["http/1.1", "h2"] and WebRTC media with ALPNs ["webrtc", "c-webrtc"]. Clients | ["http/1.1", "h2"] and WebRTC media with ALPNs ["webrtc", "c-webrtc"]. Clients | |||
| SHOULD pad this extension by rounding up to the total size of the longest ALPN | SHOULD pad this extension by rounding up to the total size of the longest ALPN | |||
| extension across all application profiles. The target padding length of most | extension across all application profiles. The target padding length of most | |||
| `ClientHello` extensions can be computed in this way. | `ClientHello` extensions can be computed in this way. | |||
| In contrast, clients do not know the longest SNI value in the client-facing | In contrast, clients do not know the longest SNI value in the client-facing | |||
| server's anonymity set without server input. Clients SHOULD use the `ECHConfig`'s | server's anonymity set without server input. Clients SHOULD use the `ECHConfig`'s | |||
| `maximum_name_length` field as follows, where L is the `maximum_name_length` | `maximum_name_length` field as follows, where M is the `maximum_name_length` | |||
| value. | value. | |||
| 1. If the `ClientHelloInner` contained a "server_name" extension with a name of | 1. If the `ClientHelloInner` contained a "server_name" extension with a name of | |||
| length D, add max(0, L - D) bytes of padding. | length D, add max(0, M - D) bytes of padding. | |||
| 2. If the `ClientHelloInner` did not contain a "server_name" extension (e.g., if | 2. If the `ClientHelloInner` did not contain a "server_name" extension (e.g., if | |||
| the client is connecting to an IP address), add L + 9 bytes of padding. This | the client is connecting to an IP address), add M + 9 bytes of padding. This | |||
| is the length of a "server_name" extension with an L-byte name. | is the length of a "server_name" extension with an M-byte name. | |||
| Finally, the client SHOULD pad the entire message as follows: | Finally, the client SHOULD pad the entire message as follows: | |||
| 1. Let L be the length of the `EncodedClientHelloInner` with all the padding | 1. Let L be the length of the `EncodedClientHelloInner` with all the padding | |||
| computed so far. | computed so far. | |||
| 2. Let N = 31 - ((L - 1) % 32) and add N bytes of padding. | 2. Let N = 31 - ((L - 1) % 32) and add N bytes of padding. | |||
| This rounds the length of `EncodedClientHelloInner` up to a multiple of 32 bytes, | This rounds the length of `EncodedClientHelloInner` up to a multiple of 32 bytes, | |||
| reducing the set of possible lengths across all clients. | reducing the set of possible lengths across all clients. | |||
| skipping to change at line 1197 ¶ | skipping to change at line 1197 ¶ | |||
| decrypt the "encrypted_client_hello" extension as follows. | decrypt the "encrypted_client_hello" extension as follows. | |||
| The server verifies that the `ECHConfig` supports the cipher suite indicated by | The server verifies that the `ECHConfig` supports the cipher suite indicated by | |||
| the `ECHClientHello.cipher_suite` and that the version of ECH indicated by the | the `ECHClientHello.cipher_suite` and that the version of ECH indicated by the | |||
| client matches the `ECHConfig.version`. If not, the server continues to the next | client matches the `ECHConfig.version`. If not, the server continues to the next | |||
| candidate `ECHConfig`. | candidate `ECHConfig`. | |||
| Next, the server decrypts `ECHClientHello.payload`, using the private key skR | Next, the server decrypts `ECHClientHello.payload`, using the private key skR | |||
| corresponding to `ECHConfig`, as follows: | corresponding to `ECHConfig`, as follows: | |||
| ~~~ | ~~ | |||
| context = SetupBaseR(ECHClientHello.enc, skR, | context = SetupBaseR(ECHClientHello.enc, skR, | |||
| "tls ech" || 0x00 || ECHConfig) | "tls ech" || 0x00 || ECHConfig) | |||
| EncodedClientHelloInner = context.Open(ClientHelloOuterAAD, | EncodedClientHelloInner = context.Open(ClientHelloOuterAAD, | |||
| ECHClientHello.payload) | ECHClientHello.payload) | |||
| ~~~ | ~~ | |||
| `ClientHelloOuterAAD` is computed from `ClientHelloOuter` as described in | `ClientHelloOuterAAD` is computed from `ClientHelloOuter` as described in | |||
| {{authenticating-outer}}. The `info` parameter to SetupBaseR is the | {{authenticating-outer}}. The `info` parameter to SetupBaseR is the | |||
| concatenation "tls ech", a zero byte, and the serialized `ECHConfig`. If | concatenation "tls ech", a zero byte, and the serialized `ECHConfig`. If | |||
| decryption fails, the server continues to the next candidate `ECHConfig`. | decryption fails, the server continues to the next candidate `ECHConfig`. | |||
| Otherwise, the server reconstructs `ClientHelloInner` from | Otherwise, the server reconstructs `ClientHelloInner` from | |||
| `EncodedClientHelloInner`, as described in {{encoding-inner}}. It then stops | `EncodedClientHelloInner`, as described in {{encoding-inner}}. It then stops | |||
| iterating over the candidate `ECHConfig` values. | iterating over the candidate `ECHConfig` values. | |||
| Once the server has chosen the correct `ECHConfig`, it MAY verify that the value | Once the server has chosen the correct `ECHConfig`, it MAY verify that the value | |||
| skipping to change at line 1272 ¶ | skipping to change at line 1272 ¶ | |||
| If the client-facing server accepted ECH, it checks that the second `ClientHelloOuter ` | If the client-facing server accepted ECH, it checks that the second `ClientHelloOuter ` | |||
| also contains the "encrypted_client_hello" extension. If not, it MUST abort the | also contains the "encrypted_client_hello" extension. If not, it MUST abort the | |||
| handshake with a "missing_extension" alert. Otherwise, it checks that | handshake with a "missing_extension" alert. Otherwise, it checks that | |||
| `ECHClientHello.cipher_suite` and `ECHClientHello.config_id` are unchanged, and that | `ECHClientHello.cipher_suite` and `ECHClientHello.config_id` are unchanged, and that | |||
| `ECHClientHello.enc` is empty. If not, it MUST abort the handshake with an | `ECHClientHello.enc` is empty. If not, it MUST abort the handshake with an | |||
| "illegal_parameter" alert. | "illegal_parameter" alert. | |||
| Finally, it decrypts the new `ECHClientHello.payload` as a second message with the | Finally, it decrypts the new `ECHClientHello.payload` as a second message with the | |||
| previous HPKE context: | previous HPKE context: | |||
| ~~~ | ~~ | |||
| EncodedClientHelloInner = context.Open(ClientHelloOuterAAD, | EncodedClientHelloInner = context.Open(ClientHelloOuterAAD, | |||
| ECHClientHello.payload) | ECHClientHello.payload) | |||
| ~~~ | ~~ | |||
| `ClientHelloOuterAAD` is computed as described in {{authenticating-outer}}, but | `ClientHelloOuterAAD` is computed as described in {{authenticating-outer}}, but | |||
| using the second `ClientHelloOuter`. If decryption fails, the client-facing | using the second `ClientHelloOuter`. If decryption fails, the client-facing | |||
| server MUST abort the handshake with a "decrypt_error" alert. Otherwise, it | server MUST abort the handshake with a "decrypt_error" alert. Otherwise, it | |||
| reconstructs the second `ClientHelloInner` from the new `EncodedClientHelloInner` | reconstructs the second `ClientHelloInner` from the new `EncodedClientHelloInner` | |||
| as described in {{encoding-inner}}, using the second `ClientHelloOuter` for | as described in {{encoding-inner}}, using the second `ClientHelloOuter` for | |||
| any referenced extensions. | any referenced extensions. | |||
| The client-facing server then forwards the resulting `ClientHelloInner` to the | The client-facing server then forwards the resulting `ClientHelloInner` to the | |||
| backend server. It forwards all subsequent TLS messages between the client and | backend server. It forwards all subsequent TLS messages between the client and | |||
| skipping to change at line 1328 ¶ | skipping to change at line 1328 ¶ | |||
| here. | here. | |||
| The backend server embeds in `ServerHello.random` a string derived from the inner | The backend server embeds in `ServerHello.random` a string derived from the inner | |||
| handshake. It begins by computing its ServerHello as usual, except the last 8 | handshake. It begins by computing its ServerHello as usual, except the last 8 | |||
| bytes of `ServerHello.random` are set to zero. It then computes the transcript | bytes of `ServerHello.random` are set to zero. It then computes the transcript | |||
| hash for `ClientHelloInner` up to and including the modified ServerHello, as | hash for `ClientHelloInner` up to and including the modified ServerHello, as | |||
| described in {{RFC8446, Section 4.4.1}}. Let transcript_ech_conf denote the | described in {{RFC8446, Section 4.4.1}}. Let transcript_ech_conf denote the | |||
| output. Finally, the backend server overwrites the last 8 bytes of the | output. Finally, the backend server overwrites the last 8 bytes of the | |||
| `ServerHello.random` with the following string: | `ServerHello.random` with the following string: | |||
| ~~~ | ~~ | |||
| accept_confirmation = HKDF-Expand-Label( | accept_confirmation = HKDF-Expand-Label( | |||
| HKDF-Extract(0, ClientHelloInner.random), | HKDF-Extract(0, ClientHelloInner.random), | |||
| "ech accept confirmation", | "ech accept confirmation", | |||
| transcript_ech_conf, | transcript_ech_conf, | |||
| 8) | 8) | |||
| ~~~ | ~~ | |||
| where HKDF-Expand-Label is defined in {{RFC8446, Section 7.1}}, "0" indicates a | where HKDF-Expand-Label is defined in {{RFC8446, Section 7.1}}, "0" indicates a | |||
| string of Hash.length bytes set to zero, and Hash is the hash function used to | string of Hash.length bytes set to zero, and Hash is the hash function used to | |||
| compute the transcript hash. In DTLS, the modified version of HKDF-Expand-Label | compute the transcript hash. In DTLS, the modified version of HKDF-Expand-Label | |||
| defined in {{RFC9147, Section 5.9}} is used instead. | defined in {{RFC9147, Section 5.9}} is used instead. | |||
| The backend server MUST NOT perform this operation if it negotiated TLS 1.2 or | The backend server MUST NOT perform this operation if it negotiated TLS 1.2 or | |||
| below. Note that doing so would overwrite the downgrade signal for TLS 1.3 (see | below. Note that doing so would overwrite the downgrade signal for TLS 1.3 (see | |||
| {{RFC8446, Section 4.1.3}}). | {{RFC8446, Section 4.1.3}}). | |||
| skipping to change at line 1361 ¶ | skipping to change at line 1361 ¶ | |||
| sends the signal in an extension. | sends the signal in an extension. | |||
| The backend server begins by computing HelloRetryRequest as usual, except that | The backend server begins by computing HelloRetryRequest as usual, except that | |||
| it also contains an "encrypted_client_hello" extension with a payload of 8 zero | it also contains an "encrypted_client_hello" extension with a payload of 8 zero | |||
| bytes. It then computes the transcript hash for the first `ClientHelloInner`, | bytes. It then computes the transcript hash for the first `ClientHelloInner`, | |||
| denoted ClientHelloInner1, up to and including the modified HelloRetryRequest. | denoted ClientHelloInner1, up to and including the modified HelloRetryRequest. | |||
| Let transcript_hrr_ech_conf denote the output. Finally, the backend server | Let transcript_hrr_ech_conf denote the output. Finally, the backend server | |||
| overwrites the payload of the "encrypted_client_hello" extension with the | overwrites the payload of the "encrypted_client_hello" extension with the | |||
| following string: | following string: | |||
| ~~~ | ~~ | |||
| hrr_accept_confirmation = HKDF-Expand-Label( | hrr_accept_confirmation = HKDF-Expand-Label( | |||
| HKDF-Extract(0, ClientHelloInner1.random), | HKDF-Extract(0, ClientHelloInner1.random), | |||
| "hrr ech accept confirmation", | "hrr ech accept confirmation", | |||
| transcript_hrr_ech_conf, | transcript_hrr_ech_conf, | |||
| 8) | 8) | |||
| ~~~ | ~~ | |||
| In the subsequent ServerHello message, the backend server sends the | In the subsequent ServerHello message, the backend server sends the | |||
| `accept_confirmation` value as described in {{backend-server}}. | `accept_confirmation` value as described in {{backend-server}}. | |||
| # Deployment Considerations {#deployment} | # Deployment Considerations {#deployment} | |||
| The design of ECH as specified in this document necessarily requires changes | The design of ECH as specified in this document necessarily requires changes | |||
| to client, client-facing server, and backend server. Coordination between | to client, client-facing server, and backend server. Coordination between | |||
| client-facing and backend server requires care, as deployment mistakes | client-facing and backend server requires care, as deployment mistakes | |||
| can lead to compatibility issues. These are discussed in {{compat-issues}}. | can lead to compatibility issues. These are discussed in {{compat-issues}}. | |||
| skipping to change at line 1895 ¶ | skipping to change at line 1895 ¶ | |||
| information about its verification process by a timing side channel), the | information about its verification process by a timing side channel), the | |||
| attacker learns that its test certificate name was incorrect. As an example, | attacker learns that its test certificate name was incorrect. As an example, | |||
| suppose the client's SNI value in its inner `ClientHello` is "example.com," and | suppose the client's SNI value in its inner `ClientHello` is "example.com," and | |||
| the attacker replied with a Certificate for "test.com". If the client produces a | the attacker replied with a Certificate for "test.com". If the client produces a | |||
| verification failure alert because of the mismatch faster than it would due to | verification failure alert because of the mismatch faster than it would due to | |||
| the Certificate signature validation, information about the name leaks. Note | the Certificate signature validation, information about the name leaks. Note | |||
| that the attacker can also withhold the CertificateVerify message. In that | that the attacker can also withhold the CertificateVerify message. In that | |||
| scenario, a client which first verifies the Certificate would then respond | scenario, a client which first verifies the Certificate would then respond | |||
| similarly and leak the same information. | similarly and leak the same information. | |||
| ~~~ | ~~ | |||
| Client Attacker Server | Client Attacker Server | |||
| ClientHello | ClientHello | |||
| + key_share | + key_share | |||
| + ech ------> (intercept) -----> X (drop) | + ech ------> (intercept) -----> X (drop) | |||
| ServerHello | ServerHello | |||
| + key_share | + key_share | |||
| {EncryptedExtensions} | {EncryptedExtensions} | |||
| {CertificateRequest*} | {CertificateRequest*} | |||
| {Certificate*} | {Certificate*} | |||
| {CertificateVerify*} | {CertificateVerify*} | |||
| <------ | <------ | |||
| Alert | Alert | |||
| ------> | ------> | |||
| ~~~ | ~~ | |||
| {: #flow-diagram-client-reaction title="Client Reaction Attack"} | {: #flow-diagram-client-reaction title="Client Reaction Attack"} | |||
| `ClientHelloInner.random` prevents this attack. In particular, since the attacker | `ClientHelloInner.random` prevents this attack. In particular, since the attacker | |||
| does not have access to this value, it cannot produce the right transcript and | does not have access to this value, it cannot produce the right transcript and | |||
| handshake keys needed for encrypting the Certificate message. Thus, the client | handshake keys needed for encrypting the Certificate message. Thus, the client | |||
| will fail to decrypt the Certificate and abort the connection. | will fail to decrypt the Certificate and abort the connection. | |||
| ### HelloRetryRequest Hijack Mitigation {#flow-hrr-hijack} | ### HelloRetryRequest Hijack Mitigation {#flow-hrr-hijack} | |||
| This attack aims to exploit server HRR state management to recover information | This attack aims to exploit server HRR state management to recover information | |||
| skipping to change at line 1932 ¶ | skipping to change at line 1932 ¶ | |||
| To begin, the attacker intercepts and forwards a legitimate `ClientHello` with an | To begin, the attacker intercepts and forwards a legitimate `ClientHello` with an | |||
| "encrypted_client_hello" (ech) extension to the server, which triggers a | "encrypted_client_hello" (ech) extension to the server, which triggers a | |||
| legitimate HelloRetryRequest in return. Rather than forward the retry to the | legitimate HelloRetryRequest in return. Rather than forward the retry to the | |||
| client, the attacker attempts to generate its own `ClientHello` in response based | client, the attacker attempts to generate its own `ClientHello` in response based | |||
| on the contents of the first `ClientHello` and HelloRetryRequest exchange with the | on the contents of the first `ClientHello` and HelloRetryRequest exchange with the | |||
| result that the server encrypts the Certificate to the attacker. If the server | result that the server encrypts the Certificate to the attacker. If the server | |||
| used the SNI from the first `ClientHello` and the key share from the second | used the SNI from the first `ClientHello` and the key share from the second | |||
| (attacker-controlled) `ClientHello`, the Certificate produced would leak the | (attacker-controlled) `ClientHello`, the Certificate produced would leak the | |||
| client's chosen SNI to the attacker. | client's chosen SNI to the attacker. | |||
| ~~~ | ~~ | |||
| Client Attacker Server | Client Attacker Server | |||
| ClientHello | ClientHello | |||
| + key_share | + key_share | |||
| + ech ------> (forward) -------> | + ech ------> (forward) -------> | |||
| HelloRetryRequest | HelloRetryRequest | |||
| + key_share | + key_share | |||
| (intercept) <------- | (intercept) <------- | |||
| ClientHello | ClientHello | |||
| + key_share' | + key_share' | |||
| + ech' -------> | + ech' -------> | |||
| ServerHello | ServerHello | |||
| + key_share | + key_share | |||
| {EncryptedExtensions} | {EncryptedExtensions} | |||
| {CertificateRequest*} | {CertificateRequest*} | |||
| {Certificate*} | {Certificate*} | |||
| {CertificateVerify*} | {CertificateVerify*} | |||
| {Finished} | {Finished} | |||
| <------- | <------- | |||
| (process server flight) | (process server flight) | |||
| ~~~ | ~~ | |||
| {: #flow-diagram-hrr-hijack title="HelloRetryRequest Hijack Attack"} | {: #flow-diagram-hrr-hijack title="HelloRetryRequest Hijack Attack"} | |||
| This attack is mitigated by using the same HPKE context for both `ClientHello` | This attack is mitigated by using the same HPKE context for both `ClientHello` | |||
| messages. The attacker does not possess the context's keys, so it cannot | messages. The attacker does not possess the context's keys, so it cannot | |||
| generate a valid encryption of the second inner `ClientHello`. | generate a valid encryption of the second inner `ClientHello`. | |||
| If the attacker could manipulate the second `ClientHello`, it might be possible | If the attacker could manipulate the second `ClientHello`, it might be possible | |||
| for the server to act as an oracle if it required parameters from the first | for the server to act as an oracle if it required parameters from the first | |||
| `ClientHello` to match that of the second `ClientHello`. For example, imagine the | `ClientHello` to match that of the second `ClientHello`. For example, imagine the | |||
| client's original SNI value in the inner `ClientHello` is "example.com", and the | client's original SNI value in the inner `ClientHello` is "example.com", and the | |||
| skipping to change at line 1986 ¶ | skipping to change at line 1986 ¶ | |||
| To begin, the attacker first interacts with a server to obtain a resumption | To begin, the attacker first interacts with a server to obtain a resumption | |||
| ticket for a given test domain, such as "example.com". Later, upon receipt of a | ticket for a given test domain, such as "example.com". Later, upon receipt of a | |||
| `ClientHelloOuter`, it modifies it such that the server will process the | `ClientHelloOuter`, it modifies it such that the server will process the | |||
| resumption ticket with `ClientHelloInner`. If the server only accepts resumption | resumption ticket with `ClientHelloInner`. If the server only accepts resumption | |||
| PSKs that match the server name, it will fail the PSK binder check with an | PSKs that match the server name, it will fail the PSK binder check with an | |||
| alert when `ClientHelloInner` is for "example.com" but silently ignore the PSK | alert when `ClientHelloInner` is for "example.com" but silently ignore the PSK | |||
| and continue when `ClientHelloInner` is for any other name. This introduces an | and continue when `ClientHelloInner` is for any other name. This introduces an | |||
| oracle for testing encrypted SNI values. | oracle for testing encrypted SNI values. | |||
| ~~~ | ~~ | |||
| Client Attacker Server | Client Attacker Server | |||
| handshake and ticket | handshake and ticket | |||
| for "example.com" | for "example.com" | |||
| <--------> | <--------> | |||
| ClientHello | ClientHello | |||
| + key_share | + key_share | |||
| + ech | + ech | |||
| + ech_outer_extensions(pre_shared_key) | + ech_outer_extensions(pre_shared_key) | |||
| skipping to change at line 2012 ¶ | skipping to change at line 2012 ¶ | |||
| + ech | + ech | |||
| + ech_outer_extensions(pre_shared_key) | + ech_outer_extensions(pre_shared_key) | |||
| + pre_shared_key' | + pre_shared_key' | |||
| --------> | --------> | |||
| Alert | Alert | |||
| -or- | -or- | |||
| ServerHello | ServerHello | |||
| ... | ... | |||
| Finished | Finished | |||
| <-------- | <-------- | |||
| ~~~ | ~~ | |||
| {: #tls-clienthello-malleability title="Message Flow for Malleable ClientHello"} | {: #tls-clienthello-malleability title="Message Flow for Malleable ClientHello"} | |||
| This attack may be generalized to any parameter which the server varies by | This attack may be generalized to any parameter which the server varies by | |||
| server name, such as ALPN preferences. | server name, such as ALPN preferences. | |||
| ECH mitigates this attack by only negotiating TLS parameters from | ECH mitigates this attack by only negotiating TLS parameters from | |||
| `ClientHelloInner` and authenticating all inputs to the `ClientHelloInner` | `ClientHelloInner` and authenticating all inputs to the `ClientHelloInner` | |||
| (`EncodedClientHelloInner` and `ClientHelloOuter`) with the HPKE AEAD. See | (`EncodedClientHelloInner` and `ClientHelloOuter`) with the HPKE AEAD. See | |||
| {{authenticating-outer}}. The decompression process in {{encoding-inner}} | {{authenticating-outer}}. The decompression process in {{encoding-inner}} | |||
| forbids "encrypted_client_hello" in OuterExtensions. This ensures the | forbids "encrypted_client_hello" in OuterExtensions. This ensures the | |||
| End of changes. 45 change blocks. | ||||
| 46 lines changed or deleted | 46 lines changed or added | |||
This html diff was produced by rfcdiff 1.48. | ||||