| rfc9849v1.txt | rfc9849.txt | |||
|---|---|---|---|---|
| Internet Engineering Task Force (IETF) E. Rescorla | Internet Engineering Task Force (IETF) E. Rescorla | |||
| Request for Comments: 9849 Independent | Request for Comments: 9849 Independent | |||
| Category: Standards Track K. Oku | Category: Standards Track K. Oku | |||
| ISSN: 2070-1721 Fastly | ISSN: 2070-1721 Fastly | |||
| N. Sullivan | N. Sullivan | |||
| Cryptography Consulting LLC | Cryptography Consulting LLC | |||
| C. A. Wood | C. A. Wood | |||
| Cloudflare | Apple | |||
| November 2025 | February 2026 | |||
| TLS Encrypted Client Hello | TLS Encrypted Client Hello | |||
| Abstract | Abstract | |||
| This document describes a mechanism in Transport Layer Security (TLS) | This document describes a mechanism in Transport Layer Security (TLS) | |||
| for encrypting a ClientHello message under a server public key. | for encrypting a ClientHello message under a server public key. | |||
| Status of This Memo | Status of This Memo | |||
| skipping to change at line 35 ¶ | skipping to change at line 35 ¶ | |||
| received public review and has been approved for publication by the | received public review and has been approved for publication by the | |||
| Internet Engineering Steering Group (IESG). Further information on | Internet Engineering Steering Group (IESG). Further information on | |||
| Internet Standards is available in Section 2 of RFC 7841. | Internet Standards is available in Section 2 of RFC 7841. | |||
| Information about the current status of this document, any errata, | Information about the current status of this document, any errata, | |||
| and how to provide feedback on it may be obtained at | and how to provide feedback on it may be obtained at | |||
| https://www.rfc-editor.org/info/rfc9849. | https://www.rfc-editor.org/info/rfc9849. | |||
| Copyright Notice | Copyright Notice | |||
| Copyright (c) 2025 IETF Trust and the persons identified as the | Copyright (c) 2026 IETF Trust and the persons identified as the | |||
| document authors. All rights reserved. | document authors. All rights reserved. | |||
| This document is subject to BCP 78 and the IETF Trust's Legal | This document is subject to BCP 78 and the IETF Trust's Legal | |||
| Provisions Relating to IETF Documents | Provisions Relating to IETF Documents | |||
| (https://trustee.ietf.org/license-info) in effect on the date of | (https://trustee.ietf.org/license-info) in effect on the date of | |||
| publication of this document. Please review these documents | publication of this document. Please review these documents | |||
| carefully, as they describe your rights and restrictions with respect | carefully, as they describe your rights and restrictions with respect | |||
| to this document. Code Components extracted from this document must | to this document. Code Components extracted from this document must | |||
| include Revised BSD License text as described in Section 4.e of the | include Revised BSD License text as described in Section 4.e of the | |||
| Trust Legal Provisions and are provided without warranty as described | Trust Legal Provisions and are provided without warranty as described | |||
| skipping to change at line 191 ¶ | skipping to change at line 191 ¶ | |||
| 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) | |||
| Figure 1: Shared Mode Topology | Figure 1: Shared Mode Topology | |||
| In Shared Mode, the provider is the origin server for all the domains | 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 | whose DNS records point to it. In this mode, the TLS connection is | |||
| terminated by the provider. | terminated by the 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 | |||
| Figure 2: Split Mode Topology | Figure 2: Split Mode Topology | |||
| In Split Mode, the provider is not the origin server for private | In split mode, the provider is not the origin server for private | |||
| domains. Rather, the DNS records for private domains point to the | domains. Rather, the DNS records for private domains point to the | |||
| provider, and the provider's server relays the connection back to the | provider, and the provider's server relays the connection back to the | |||
| origin server, who terminates the TLS connection with the client. | origin server, who terminates the TLS connection with the client. | |||
| Importantly, the service provider does not have access to the | Importantly, the service provider does not have access to the | |||
| plaintext of the connection beyond the unencrypted portions of the | plaintext of the connection beyond the unencrypted portions of the | |||
| handshake. | handshake. | |||
| In the remainder of this document, we will refer to the ECH-service | In the remainder of this document, we will refer to the ECH-service | |||
| provider as the "client-facing server" and the TLS terminator as the | provider as the "client-facing server" and to the TLS terminator as | |||
| "backend server". These are the same entity in Shared Mode, but in | the "backend server". These are the same entity in shared mode, but | |||
| Split Mode, the client-facing and backend servers are physically | in split mode, the client-facing and backend servers are physically | |||
| separated. | separated. | |||
| See Section 10 for more discussion about the ECH threat model and how | See Section 10 for more discussion about the ECH threat model and how | |||
| it relates to the client, client-facing server, and backend server. | it relates to the client, client-facing server, and backend server. | |||
| 3.2. Encrypted ClientHello (ECH) | 3.2. Encrypted ClientHello (ECH) | |||
| A client-facing server enables ECH by publishing an ECH | A client-facing server enables ECH by publishing an ECH | |||
| configuration, which is an encryption public key and associated | configuration, which is an encryption public key and associated | |||
| metadata. Domains which wish to use ECH must publish this | metadata. Domains which wish to use ECH must publish this | |||
| skipping to change at line 433 ¶ | skipping to change at line 433 ¶ | |||
| enabler for authenticated key mismatch signals (see Section 7). In | enabler for authenticated key mismatch signals (see Section 7). In | |||
| contrast, the inner ClientHello is the true ClientHello used upon ECH | contrast, the inner ClientHello is the true ClientHello used upon ECH | |||
| negotiation. | negotiation. | |||
| 5. The "encrypted_client_hello" Extension | 5. The "encrypted_client_hello" Extension | |||
| To offer ECH, the client sends an "encrypted_client_hello" extension | To offer ECH, the client sends an "encrypted_client_hello" extension | |||
| in the ClientHelloOuter. When it does, it MUST also send the | in the ClientHelloOuter. When it does, it MUST also send the | |||
| extension in ClientHelloInner. | extension in ClientHelloInner. | |||
| enum { | ~~ enum { encrypted_client_hello(0xfe0d), (65535) } ExtensionType; ~~ | |||
| encrypted_client_hello(0xfe0d), (65535) | ||||
| } 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; | |||
| skipping to change at line 479 ¶ | skipping to change at line 477 ¶ | |||
| ClientHelloOuter sent in response to HelloRetryRequest. | ClientHelloOuter sent in response to HelloRetryRequest. | |||
| payload: The serialized and encrypted EncodedClientHelloInner | payload: The serialized and encrypted EncodedClientHelloInner | |||
| structure, encrypted using HPKE as described in Section 6.1. | structure, encrypted using HPKE as described in Section 6.1. | |||
| 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, the server MAY include an "encrypted_client_hello" | |||
| extension in its EncryptedExtensions message, as described in | extension in its EncryptedExtensions message, as described in | |||
| Section 7.1, with the following payload: | Section 7.1, with the following payload: | |||
| struct { | ~~ struct { ECHConfigList retry_configs; } ECHEncryptedExtensions; ~~ | |||
| ECHConfigList retry_configs; | ||||
| } ECHEncryptedExtensions; | ||||
| The response is valid only when the server used the ClientHelloOuter. | The response is valid only when the server used the ClientHelloOuter. | |||
| If the server sent this extension in response to the inner variant, | If the server sent this extension in response to the inner variant, | |||
| then the client MUST abort with an "unsupported_extension" alert. | then the client MUST abort with an "unsupported_extension" alert. | |||
| retry_configs: An ECHConfigList structure containing one or more | retry_configs: An ECHConfigList structure containing one or more | |||
| ECHConfig structures, in decreasing order of preference, to be | ECHConfig structures, in decreasing order of preference, to be | |||
| used by the client as described in Section 6.1.6. These are known | used by the client as described in Section 6.1.6. These are known | |||
| as the server's "retry configurations". | as the server's "retry configurations". | |||
| Finally, when the client offers the "encrypted_client_hello", if the | Finally, when the client offers the "encrypted_client_hello", if the | |||
| payload is the inner variant and the server responds with | payload is the inner variant and the server responds with | |||
| HelloRetryRequest, it MUST include an "encrypted_client_hello" | HelloRetryRequest, it MUST include an "encrypted_client_hello" | |||
| extension with the following payload: | extension with the following payload: | |||
| struct { | ~~ struct { opaque confirmation[8]; } ECHHelloRetryRequest; ~~ | |||
| opaque confirmation[8]; | ||||
| } ECHHelloRetryRequest; | ||||
| The value of ECHHelloRetryRequest.confirmation is set to | The value of ECHHelloRetryRequest.confirmation is set to | |||
| hrr_accept_confirmation as described in Section 7.2.1. | hrr_accept_confirmation as described in Section 7.2.1. | |||
| This document also defines the "ech_required" alert, which the client | This document also defines the "ech_required" alert, which the client | |||
| MUST send when it offered an "encrypted_client_hello" extension that | MUST send when it offered an "encrypted_client_hello" extension that | |||
| was not accepted by the server. (See Section 11.2.) | was not accepted by the server. (See Section 11.2.) | |||
| 5.1. Encoding the ClientHelloInner | 5.1. Encoding the ClientHelloInner | |||
| Before encrypting, the client pads and optionally compresses | Before encrypting, the client pads and optionally compresses | |||
| ClientHelloInner into an EncodedClientHelloInner structure, defined | ClientHelloInner into an EncodedClientHelloInner structure, defined | |||
| below: | below: | |||
| struct { | ~~ struct { ClientHello client_hello; uint8 zeros[length_of_padding]; | |||
| ClientHello client_hello; | } EncodedClientHelloInner; ~~ | |||
| uint8 zeros[length_of_padding]; | ||||
| } EncodedClientHelloInner; | ||||
| The client_hello field is computed by first making a copy of | The client_hello field is computed by first making a copy of | |||
| ClientHelloInner and setting the legacy_session_id field to the empty | ClientHelloInner and setting the legacy_session_id field to the empty | |||
| string. In TLS, this field uses the ClientHello structure defined in | string. In TLS, this field uses the ClientHello structure defined in | |||
| Section 4.1.2 of [RFC8446]. In DTLS, it uses the ClientHello | Section 4.1.2 of [RFC8446]. In DTLS, it uses the ClientHello | |||
| structured defined in Section 5.3 of [RFC9147]. This does not | structure defined in Section 5.3 of [RFC9147]. This does not include | |||
| include Handshake structure's four-byte header in TLS, nor twelve- | Handshake structure's four-byte header in TLS, nor twelve-byte header | |||
| byte header in DTLS. The zeros field MUST be all zeroes of length | in DTLS. The zeros field MUST be all zeroes of length | |||
| length_of_padding (see Section 6.1.3). | length_of_padding (see Section 6.1.3). | |||
| Repeating large extensions, such as "key_share" with post-quantum | Repeating large extensions, such as "key_share" with post-quantum | |||
| algorithms, between ClientHelloInner and ClientHelloOuter can lead to | algorithms, between ClientHelloInner and ClientHelloOuter can lead to | |||
| excessive size. To reduce the size impact, the client MAY substitute | excessive size. To reduce the size impact, the client MAY substitute | |||
| extensions which it knows will be duplicated in ClientHelloOuter. It | extensions which it knows will be duplicated in ClientHelloOuter. It | |||
| does so by removing and replacing extensions from | does so by removing and replacing extensions from | |||
| EncodedClientHelloInner with a single "ech_outer_extensions" | EncodedClientHelloInner with a single "ech_outer_extensions" | |||
| extension, defined as follows: | extension, defined as follows: | |||
| enum { | ~~ enum { ech_outer_extensions(0xfd00), (65535) } ExtensionType; | |||
| ech_outer_extensions(0xfd00), (65535) | ||||
| } ExtensionType; | ||||
| ExtensionType OuterExtensions<2..254>; | ExtensionType OuterExtensions<2..254>; ~~ | |||
| OuterExtensions contains the removed ExtensionType values. Each | OuterExtensions contains the removed ExtensionType values. Each | |||
| value references the matching extension in ClientHelloOuter. The | value references the matching extension in ClientHelloOuter. The | |||
| values MUST be ordered contiguously in ClientHelloInner, and the | values MUST be ordered contiguously in ClientHelloInner, and the | |||
| "ech_outer_extensions" extension MUST be inserted in the | "ech_outer_extensions" extension MUST be inserted in the | |||
| corresponding position in EncodedClientHelloInner. Additionally, the | corresponding position in EncodedClientHelloInner. Additionally, the | |||
| extensions MUST appear in ClientHelloOuter in the same relative | extensions MUST appear in ClientHelloOuter in the same relative | |||
| order. However, there is no requirement that they be contiguous. | order. However, there is no requirement that they be contiguous. | |||
| For example, OuterExtensions may contain extensions A, B, and C, | For example, OuterExtensions may contain extensions A, B, and C, | |||
| while ClientHelloOuter contains extensions A, D, B, C, E, and F. | while ClientHelloOuter contains extensions A, D, B, C, E, and F. | |||
| skipping to change at line 627 ¶ | skipping to change at line 617 ¶ | |||
| in possession of a compatible ECH configuration and sends GREASE ECH | in possession of a compatible ECH configuration and sends GREASE ECH | |||
| (see Section 6.2) otherwise. | (see Section 6.2) otherwise. | |||
| 6.1. Offering ECH | 6.1. Offering ECH | |||
| To offer ECH, the client first chooses a suitable ECHConfig from the | To offer ECH, the client first chooses a suitable ECHConfig from the | |||
| server's ECHConfigList. To determine if a given ECHConfig is | server's ECHConfigList. To determine if a given ECHConfig is | |||
| suitable, it checks that it supports the KEM algorithm identified by | suitable, it checks that it supports the KEM algorithm identified by | |||
| ECHConfig.contents.kem_id, at least one KDF/AEAD algorithm identified | ECHConfig.contents.kem_id, at least one KDF/AEAD algorithm identified | |||
| by ECHConfig.contents.cipher_suites, and the version of ECH indicated | by ECHConfig.contents.cipher_suites, and the version of ECH indicated | |||
| by ECHConfig.contents.version. Once a suitable configuration is | by ECHConfig.version. Once a suitable configuration is found, the | |||
| found, the client selects the cipher suite it will use for | client selects the cipher suite it will use for encryption. It MUST | |||
| encryption. It MUST NOT choose a cipher suite or version not | NOT choose a cipher suite or version not advertised by the | |||
| advertised by the configuration. If no compatible configuration is | configuration. If no compatible configuration is found, then the | |||
| found, then the client SHOULD proceed as described in Section 6.2. | client SHOULD proceed as described in Section 6.2. | |||
| Next, the client constructs the ClientHelloInner message just as it | Next, the client constructs the ClientHelloInner message just as it | |||
| does a standard ClientHello, with the exception of the following | does a standard ClientHello, with the exception of the following | |||
| rules: | rules: | |||
| 1. It MUST NOT offer to negotiate TLS 1.2 or below. This is | 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 | necessary to ensure the backend server does not negotiate a TLS | |||
| version that is incompatible with ECH. | version that is incompatible with ECH. | |||
| 2. It MUST NOT offer to resume any session for TLS 1.2 and below. | 2. It MUST NOT offer to resume any session for TLS 1.2 and below. | |||
| skipping to change at line 655 ¶ | skipping to change at line 645 ¶ | |||
| 4. It MUST include the "encrypted_client_hello" extension of type | 4. It MUST include the "encrypted_client_hello" extension of type | |||
| inner as described in Section 5. (This requirement is not | inner as described in Section 5. (This requirement is not | |||
| applicable when the "encrypted_client_hello" extension is | applicable when the "encrypted_client_hello" extension is | |||
| generated as described in Section 6.2.) | generated as described in Section 6.2.) | |||
| The client then constructs EncodedClientHelloInner as described in | The client then constructs EncodedClientHelloInner as described in | |||
| Section 5.1. It also computes an HPKE encryption context and enc | Section 5.1. It also computes an HPKE encryption context and enc | |||
| value as: | value as: | |||
| pkR = DeserializePublicKey(ECHConfig.contents.public_key) | ~~ pkR = DeserializePublicKey(ECHConfig.contents.public_key) enc, | |||
| enc, context = SetupBaseS(pkR, | context = SetupBaseS(pkR, "tls ech" || 0x00 || ECHConfig) ~~ | |||
| "tls ech" || 0x00 || ECHConfig) | ||||
| Next, it constructs a partial ClientHelloOuterAAD as it does a | Next, it constructs a partial ClientHelloOuterAAD 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 offer to negotiate TLS 1.3 or above. | 1. It MUST offer to negotiate TLS 1.3 or above. | |||
| 2. If it compressed any extensions in EncodedClientHelloInner, it | 2. If it compressed any extensions in EncodedClientHelloInner, it | |||
| MUST copy the corresponding extensions from ClientHelloInner. | MUST copy the corresponding extensions from ClientHelloInner. | |||
| The copied extensions additionally MUST be in the same relative | The copied extensions additionally MUST be in the same relative | |||
| order as in ClientHelloInner. | order as in ClientHelloInner. | |||
| 3. It MUST copy the legacy_session_id field from ClientHelloInner. | 3. It MUST copy the legacy_session_id field from ClientHelloInner. | |||
| This allows the server to echo the correct session ID for TLS | This allows the server to echo the correct session ID for TLS | |||
| 1.3's compatibility mode (see Appendix D.4 of [RFC8446]) when ECH | 1.3's compatibility mode (see Appendix D.4 of [RFC8446]) when ECH | |||
| is negotiated. Note that compatibility mode is not used in DTLS | is negotiated. Note that compatibility mode is not used in DTLS | |||
| 1.3, but following this rule will produce the correct results for | 1.3, but following this rule will produce the correct results for | |||
| both TLS 1.3 and DTLS 1.3. | both TLS 1.3 and DTLS 1.3. | |||
| 4. It MAY copy any other field from the ClientHelloInner except | 4. It MAY copy any other field from the ClientHelloInner except | |||
| ClientHelloInner.random. Instead, It MUST generate a fresh | ClientHelloInner.random. Instead, it MUST generate a fresh | |||
| ClientHelloOuter.random using a secure random number generator. | ClientHelloOuter.random using a secure random number generator. | |||
| (See Section 10.12.1.) | (See Section 10.12.1.) | |||
| 5. It SHOULD place the value of ECHConfig.contents.public_name in | 5. It SHOULD place the value of ECHConfig.contents.public_name in | |||
| the "server_name" extension. Clients that do not follow this | the "server_name" extension. Clients that do not follow this | |||
| step, or place a different value in the "server_name" extension, | step, or place a different value in the "server_name" extension, | |||
| risk breaking the retry mechanism described in Section 6.1.6 or | risk breaking the retry mechanism described in Section 6.1.6 or | |||
| failing to interoperate with servers that require this step to be | failing to interoperate with servers that require this step to be | |||
| done; see Section 7.1. | done; see Section 7.1. | |||
| 6. When the client offers the "pre_shared_key" extension in | 6. When the client offers the "pre_shared_key" extension in | |||
| ClientHelloInner, it SHOULD also include a GREASE | ClientHelloInner, it SHOULD also include a GREASE | |||
| "pre_shared_key" extension in ClientHelloOuter, generated in the | "pre_shared_key" extension in ClientHelloOuter, generated in the | |||
| manner described in Section 6.1.2. The client MUST NOT use this | manner described in Section 6.1.2. The client MUST NOT use this | |||
| extension to advertise a Pre-Shared Key (PSK) to the client- | extension to advertise a PSK to the client-facing server. (See | |||
| facing server. (See Section 10.12.3.) When the client includes | Section 10.12.3.) When the client includes a GREASE | |||
| a GREASE "pre_shared_key" extension, it MUST also copy the | "pre_shared_key" extension, it MUST also copy the | |||
| "psk_key_exchange_modes" from the ClientHelloInner into the | "psk_key_exchange_modes" from the ClientHelloInner into the | |||
| ClientHelloOuter. | ClientHelloOuter. | |||
| 7. When the client offers the "early_data" extension in | 7. When the client offers the "early_data" extension in | |||
| ClientHelloInner, it MUST also include the "early_data" extension | ClientHelloInner, it MUST also include the "early_data" extension | |||
| in ClientHelloOuter. This allows servers that reject ECH and use | in ClientHelloOuter. This allows servers that reject ECH and use | |||
| ClientHelloOuter to safely ignore any early data sent by the | ClientHelloOuter to safely ignore any early data sent by the | |||
| client per [RFC8446], Section 4.2.10. | client per [RFC8446], Section 4.2.10. | |||
| The client might duplicate non-sensitive extensions in both messages. | The client might duplicate non-sensitive extensions in both messages. | |||
| skipping to change at line 744 ¶ | skipping to change at line 733 ¶ | |||
| * payload, a placeholder byte string containing L zeros. | * payload, a placeholder byte string containing L zeros. | |||
| If configuration identifiers (see Section 10.4) are to be ignored, | If configuration identifiers (see Section 10.4) are to be ignored, | |||
| config_id SHOULD be set to a randomly generated byte in the first | config_id SHOULD be set to a randomly generated byte in the first | |||
| ClientHelloOuter and, in the event of a HelloRetryRequest (HRR), MUST | ClientHelloOuter and, in the event of a HelloRetryRequest (HRR), MUST | |||
| be left unchanged for the second ClientHelloOuter. | be left unchanged for the second ClientHelloOuter. | |||
| The client serializes this structure to construct the | The client serializes this structure to construct the | |||
| ClientHelloOuterAAD. It then computes the final payload as: | ClientHelloOuterAAD. 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 | Including ClientHelloOuterAAD as the HPKE AAD binds the | |||
| ClientHelloOuter to the ClientHelloInner, thus preventing attackers | ClientHelloOuter to the ClientHelloInner, thus preventing attackers | |||
| from modifying ClientHelloOuter while keeping the same | from modifying ClientHelloOuter while keeping the same | |||
| ClientHelloInner, as described in Section 10.12.3. | ClientHelloInner, as described in Section 10.12.3. | |||
| 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 | ClientHelloOuter. The two values have the same length, so it is not | |||
| necessary to recompute length prefixes in the serialized structure. | necessary to recompute length prefixes in the serialized structure. | |||
| skipping to change at line 812 ¶ | skipping to change at line 801 ¶ | |||
| By way of example, clients typically support a small number of | By way of example, clients typically support a small number of | |||
| application profiles. For instance, a browser might support HTTP | application profiles. For instance, a browser might support HTTP | |||
| with ALPN values ["http/1.1", "h2"] and WebRTC media with ALPNs | with ALPN values ["http/1.1", "h2"] and WebRTC media with ALPNs | |||
| ["webrtc", "c-webrtc"]. Clients SHOULD pad this extension by | ["webrtc", "c-webrtc"]. Clients SHOULD pad this extension by | |||
| rounding up to the total size of the longest ALPN extension across | rounding up to the total size of the longest ALPN extension across | |||
| all application profiles. The target padding length of most | 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- | In contrast, clients do not know the longest SNI value in the client- | |||
| facing server's anonymity set without server input. Clients SHOULD | facing server's anonymity set without server input. Clients SHOULD | |||
| use the ECHConfig's maximum_name_length field as follows, where L is | use the ECHConfig's maximum_name_length field as follows, where M is | |||
| the maximum_name_length value. | the maximum_name_length value. | |||
| 1. If the ClientHelloInner contained a "server_name" extension with | 1. If the ClientHelloInner contained a "server_name" extension with | |||
| a name of length D, add max(0, L - D) bytes of padding. | a name of length D, add max(0, M - D) bytes of padding. | |||
| 2. If the ClientHelloInner did not contain a "server_name" extension | 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 | (e.g., if the client is connecting to an IP address), add M + 9 | |||
| bytes of padding. This is the length of a "server_name" | bytes of padding. This is the length of a "server_name" | |||
| extension with an L-byte 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 | 1. Let L be the length of the EncodedClientHelloInner with all the | |||
| padding computed so far. | padding 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 | This rounds the length of EncodedClientHelloInner up to a multiple of | |||
| 32 bytes, reducing the set of possible lengths across all clients. | 32 bytes, reducing the set of possible lengths across all clients. | |||
| skipping to change at line 977 ¶ | skipping to change at line 966 ¶ | |||
| connection and a node with configuration B in the second. Note that | connection and a node with configuration B in the second. Note that | |||
| this guidance does not apply to the cases in the previous paragraph | this guidance does not apply to the cases in the previous paragraph | |||
| where the server has securely disabled ECH. | where the server has securely disabled ECH. | |||
| If a client does not retry, it MUST report an error to the calling | If a client does not retry, it MUST report an error to the calling | |||
| application. | application. | |||
| 6.1.7. Authenticating for the Public Name | 6.1.7. Authenticating for the Public Name | |||
| When the server rejects ECH, it continues with the handshake using | When the server rejects ECH, it continues with the handshake using | |||
| the plaintext "server_name" extension instead (see Section 7). Then, | the plaintext "server_name" extension instead (see Section 7). | |||
| clients that offer ECH authenticate the connection with the public | Clients that offer ECH then authenticate the connection with the | |||
| name as follows: | public name as follows: | |||
| * The client MUST verify that the certificate is valid for | * The client MUST verify that the certificate is valid for | |||
| ECHConfig.contents.public_name. If invalid, it MUST abort the | ECHConfig.contents.public_name. If invalid, it MUST abort the | |||
| connection with the appropriate alert. | connection with the appropriate alert. | |||
| * If the server requests a client certificate, the client MUST | * If the server requests a client certificate, the client MUST | |||
| respond with an empty Certificate message, denoting no client | respond with an empty Certificate message, denoting no client | |||
| certificate. | certificate. | |||
| In verifying the client-facing server certificate, the client MUST | In verifying the client-facing server certificate, the client MUST | |||
| interpret the public name as a DNS-based reference identity | interpret the public name as a DNS-based reference identity | |||
| [RFC6125]. Clients that incorporate DNS names and IP addresses into | [RFC9525]. Clients that incorporate DNS names and IP addresses into | |||
| the same syntax (e.g. Section 7.4 of [RFC3986] and [WHATWG-IPV4]) | the same syntax (e.g. Section 7.4 of [RFC3986] and [WHATWG-IPV4]) | |||
| MUST reject names that would be interpreted as IPv4 addresses. | MUST reject names that would be interpreted as IPv4 addresses. | |||
| Clients that enforce this by checking ECHConfig.contents.public_name | Clients that enforce this by checking ECHConfig.contents.public_name | |||
| do not need to repeat the check when processing ECH rejection. | do not need to repeat the check when processing ECH rejection. | |||
| Note that authenticating a connection for the public name does not | Note that authenticating a connection for the public name does not | |||
| authenticate it for the origin. The TLS implementation MUST NOT | authenticate it for the origin. The TLS implementation MUST NOT | |||
| report such connections as successful to the application. It | report such connections as successful to the application. It | |||
| additionally MUST ignore all session tickets and session IDs | additionally MUST ignore all session tickets and session IDs | |||
| presented by the server. These connections are only used to trigger | presented by the server. These connections are only used to trigger | |||
| skipping to change at line 1131 ¶ | skipping to change at line 1120 ¶ | |||
| application-level warning message when these are observed. | application-level warning message when these are observed. | |||
| * By giving the extraneous configurations an invalid public key and | * By giving the extraneous configurations an invalid public key and | |||
| a public name not associated with the server so that the initial | a public name not associated with the server so that the initial | |||
| ClientHelloOuter will not be decryptable and the server cannot | ClientHelloOuter will not be decryptable and the server cannot | |||
| perform the recovery flow described in Section 6.1.6. | perform the recovery flow described in Section 6.1.6. | |||
| 7. Server Behavior | 7. Server Behavior | |||
| As described in Section 3.1, servers can play two roles, either as | As described in Section 3.1, servers can play two roles, either as | |||
| the client-facing server or as the back-end server. Depending on the | the client-facing server or as the backend server. Depending on the | |||
| server role, the ECHClientHello will be different: | server role, the ECHClientHello will be different: | |||
| * A client-facing server expects an ECHClientHello.type of outer, | * A client-facing server expects an ECHClientHello.type of outer, | |||
| and proceeds as described in Section 7.1 to extract a | and proceeds as described in Section 7.1 to extract a | |||
| ClientHelloInner, if available. | ClientHelloInner, if available. | |||
| * A backend server expects an ECHClientHello.type of inner, and | * A backend server expects an ECHClientHello.type of inner, and | |||
| proceeds as described in Section 7.2. | proceeds as described in Section 7.2. | |||
| In split mode, a client-facing server which receives a ClientHello | In split mode, a client-facing server which receives a ClientHello | |||
| skipping to change at line 1198 ¶ | skipping to change at line 1187 ¶ | |||
| follows. | follows. | |||
| The server verifies that the ECHConfig supports the cipher suite | The server verifies that the ECHConfig supports the cipher suite | |||
| indicated by the ECHClientHello.cipher_suite and that the version of | indicated by the ECHClientHello.cipher_suite and that the version of | |||
| ECH indicated by the client matches the ECHConfig.version. If not, | ECH indicated by the client matches the ECHConfig.version. If not, | |||
| the server continues to the next candidate ECHConfig. | the server continues to the next candidate ECHConfig. | |||
| Next, the server decrypts ECHClientHello.payload, using the private | Next, the server decrypts ECHClientHello.payload, using the private | |||
| key skR corresponding to ECHConfig, as follows: | key skR corresponding to ECHConfig, as follows: | |||
| context = SetupBaseR(ECHClientHello.enc, skR, | ~~ context = SetupBaseR(ECHClientHello.enc, skR, "tls ech" || 0x00 || | |||
| "tls ech" || 0x00 || ECHConfig) | ECHConfig) EncodedClientHelloInner = | |||
| EncodedClientHelloInner = context.Open(ClientHelloOuterAAD, | context.Open(ClientHelloOuterAAD, ECHClientHello.payload) ~~ | |||
| ECHClientHello.payload) | ||||
| ClientHelloOuterAAD is computed from ClientHelloOuter as described in | ClientHelloOuterAAD is computed from ClientHelloOuter as described in | |||
| Section 5.2. The info parameter to SetupBaseR is the concatenation | Section 5.2. The info parameter to SetupBaseR is the concatenation | |||
| "tls ech", a zero byte, and the serialized ECHConfig. If decryption | "tls ech", a zero byte, and the serialized ECHConfig. If decryption | |||
| fails, the server continues to the next candidate ECHConfig. | 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 Section 5.1. It then stops | EncodedClientHelloInner, as described in Section 5.1. 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 | Once the server has chosen the correct ECHConfig, it MAY verify that | |||
| skipping to change at line 1277 ¶ | skipping to change at line 1265 ¶ | |||
| ClientHelloOuter also contains the "encrypted_client_hello" | ClientHelloOuter also contains the "encrypted_client_hello" | |||
| extension. If not, it MUST abort the handshake with a | extension. If not, it MUST abort the handshake with a | |||
| "missing_extension" alert. Otherwise, it checks that | "missing_extension" alert. Otherwise, it checks that | |||
| ECHClientHello.cipher_suite and ECHClientHello.config_id are | ECHClientHello.cipher_suite and ECHClientHello.config_id are | |||
| unchanged, and that ECHClientHello.enc is empty. If not, it MUST | unchanged, and that ECHClientHello.enc is empty. If not, it MUST | |||
| abort the handshake with an "illegal_parameter" alert. | abort the handshake with an "illegal_parameter" alert. | |||
| Finally, it decrypts the new ECHClientHello.payload as a second | Finally, it decrypts the new ECHClientHello.payload as a second | |||
| message with the previous HPKE context: | message with the previous HPKE context: | |||
| EncodedClientHelloInner = context.Open(ClientHelloOuterAAD, | ~~ EncodedClientHelloInner = context.Open(ClientHelloOuterAAD, | |||
| ECHClientHello.payload) | ECHClientHello.payload) ~~ | |||
| ClientHelloOuterAAD is computed as described in Section 5.2, but | ClientHelloOuterAAD is computed as described in Section 5.2, but | |||
| using the second ClientHelloOuter. If decryption fails, the client- | using the second ClientHelloOuter. If decryption fails, the client- | |||
| facing server MUST abort the handshake with a "decrypt_error" alert. | facing server MUST abort the handshake with a "decrypt_error" alert. | |||
| Otherwise, it reconstructs the second ClientHelloInner from the new | Otherwise, it reconstructs the second ClientHelloInner from the new | |||
| EncodedClientHelloInner as described in Section 5.1, using the second | EncodedClientHelloInner as described in Section 5.1, using the second | |||
| ClientHelloOuter for any referenced extensions. | ClientHelloOuter for any referenced extensions. | |||
| The client-facing server then forwards the resulting ClientHelloInner | The client-facing server then forwards the resulting ClientHelloInner | |||
| to the backend server. It forwards all subsequent TLS messages | to the backend server. It forwards all subsequent TLS messages | |||
| skipping to change at line 1324 ¶ | skipping to change at line 1312 ¶ | |||
| The backend server embeds in ServerHello.random a string derived from | The backend server embeds in ServerHello.random a string derived from | |||
| the inner handshake. It begins by computing its ServerHello as | the inner handshake. It begins by computing its ServerHello as | |||
| usual, except the last 8 bytes of ServerHello.random are set to zero. | usual, except the last 8 bytes of ServerHello.random are set to zero. | |||
| It then computes the transcript hash for ClientHelloInner up to and | It then computes the transcript hash for ClientHelloInner up to and | |||
| including the modified ServerHello, as described in [RFC8446], | including the modified ServerHello, as described in [RFC8446], | |||
| Section 4.4.1. Let transcript_ech_conf denote the output. Finally, | Section 4.4.1. Let transcript_ech_conf denote the output. Finally, | |||
| the backend server overwrites the last 8 bytes of the | 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, | |||
| HKDF-Extract(0, ClientHelloInner.random), | ClientHelloInner.random), "ech accept confirmation", | |||
| "ech accept confirmation", | transcript_ech_conf, 8) ~~ | |||
| transcript_ech_conf, | ||||
| 8) | ||||
| where HKDF-Expand-Label is defined in [RFC8446], Section 7.1, "0" | 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 | indicates a string of Hash.length bytes set to zero, and Hash is the | |||
| hash function used to compute the transcript hash. In DTLS, the | hash function used to compute the transcript hash. In DTLS, the | |||
| modified version of HKDF-Expand-Label defined in [RFC9147], | modified version of HKDF-Expand-Label defined in [RFC9147], | |||
| Section 5.9 is used instead. | Section 5.9 is used instead. | |||
| The backend server MUST NOT perform this operation if it negotiated | The backend server MUST NOT perform this operation if it negotiated | |||
| TLS 1.2 or below. Note that doing so would overwrite the downgrade | TLS 1.2 or below. Note that doing so would overwrite the downgrade | |||
| signal for TLS 1.3 (see [RFC8446], Section 4.1.3). | signal for TLS 1.3 (see [RFC8446], Section 4.1.3). | |||
| skipping to change at line 1358 ¶ | skipping to change at line 1344 ¶ | |||
| The backend server begins by computing HelloRetryRequest as usual, | The backend server begins by computing HelloRetryRequest as usual, | |||
| except that it also contains an "encrypted_client_hello" extension | except that it also contains an "encrypted_client_hello" extension | |||
| with a payload of 8 zero bytes. It then computes the transcript hash | with a payload of 8 zero bytes. It then computes the transcript hash | |||
| for the first ClientHelloInner, denoted ClientHelloInner1, up to and | for the first ClientHelloInner, denoted ClientHelloInner1, up to and | |||
| including the modified HelloRetryRequest. Let | including the modified HelloRetryRequest. Let | |||
| transcript_hrr_ech_conf denote the output. Finally, the backend | transcript_hrr_ech_conf denote the output. Finally, the backend | |||
| server overwrites the payload of the "encrypted_client_hello" | server overwrites the payload of the "encrypted_client_hello" | |||
| extension with the following string: | extension with the following string: | |||
| hrr_accept_confirmation = HKDF-Expand-Label( | ~~ hrr_accept_confirmation = HKDF-Expand-Label( HKDF-Extract(0, | |||
| HKDF-Extract(0, ClientHelloInner1.random), | ClientHelloInner1.random), "hrr ech accept confirmation", | |||
| "hrr ech accept confirmation", | transcript_hrr_ech_conf, 8) ~~ | |||
| transcript_hrr_ech_conf, | ||||
| 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 Section 7.2. | accept_confirmation value as described in Section 7.2. | |||
| 8. Deployment Considerations | 8. Deployment Considerations | |||
| The design of ECH as specified in this document necessarily requires | The design of ECH as specified in this document necessarily requires | |||
| changes to client, client-facing server, and backend server. | changes to client, client-facing server, and backend server. | |||
| Coordination between client-facing and backend server requires care, | Coordination between client-facing and backend server requires care, | |||
| as deployment mistakes can lead to compatibility issues. These are | as deployment mistakes can lead to compatibility issues. These are | |||
| skipping to change at line 1501 ¶ | skipping to change at line 1485 ¶ | |||
| A middlebox that filters based on plaintext packet contents is one | A middlebox that filters based on plaintext packet contents is one | |||
| example of a passive attacker. In contrast, active attackers can | example of a passive attacker. In contrast, active attackers can | |||
| also write packets into the network for malicious purposes, such as | also write packets into the network for malicious purposes, such as | |||
| interfering with existing connections, probing servers, and querying | interfering with existing connections, probing servers, and querying | |||
| DNS. In short, an active attacker corresponds to the conventional | DNS. In short, an active attacker corresponds to the conventional | |||
| threat model [RFC3552] for TLS 1.3 [RFC8446]. | threat model [RFC3552] for TLS 1.3 [RFC8446]. | |||
| Passive and active attackers can exist anywhere in the network, | Passive and active attackers can exist anywhere in the network, | |||
| including between the client and client-facing server, as well as | including between the client and client-facing server, as well as | |||
| between the client-facing and backend servers when running ECH in | between the client-facing and backend servers when running ECH in | |||
| Split Mode. However, for Split Mode in particular, ECH makes two | split mode. However, for split mode in particular, ECH makes two | |||
| additional assumptions: | additional assumptions: | |||
| 1. The channel between each client-facing and each backend server is | 1. The channel between each client-facing and each backend server is | |||
| authenticated such that the backend server only accepts messages | authenticated such that the backend server only accepts messages | |||
| from trusted client-facing servers. The exact mechanism for | from trusted client-facing servers. The exact mechanism for | |||
| establishing this authenticated channel is out of scope for this | establishing this authenticated channel is out of scope for this | |||
| document. | document. | |||
| 2. The attacker cannot correlate messages between a client and | 2. The attacker cannot correlate messages between a client and | |||
| client-facing server with messages between client-facing and | client-facing server with messages between client-facing and | |||
| skipping to change at line 1709 ¶ | skipping to change at line 1693 ¶ | |||
| adversary that observes this can deduce that the ECH-enabled | adversary that observes this can deduce that the ECH-enabled | |||
| connection was made to a host that the client previously connected to | connection was made to a host that the client previously connected to | |||
| and which is within the same anonymity set. | and which is within the same anonymity set. | |||
| 10.8. Cookies | 10.8. Cookies | |||
| Section 4.2.2 of [RFC8446] defines a cookie value that servers may | Section 4.2.2 of [RFC8446] defines a cookie value that servers may | |||
| send in HelloRetryRequest for clients to echo in the second | send in HelloRetryRequest for clients to echo in the second | |||
| ClientHello. While ECH encrypts the cookie in the second | ClientHello. While ECH encrypts the cookie in the second | |||
| ClientHelloInner, the backend server's HelloRetryRequest is | ClientHelloInner, the backend server's HelloRetryRequest is | |||
| unencrypted.This means differences in cookies between backend | unencrypted. This means differences in cookies between backend | |||
| servers, such as lengths or cleartext components, may leak | servers, such as lengths or cleartext components, may leak | |||
| information about the server identity. | information about the server identity. | |||
| Backend servers in an anonymity set SHOULD NOT reveal information in | Backend servers in an anonymity set SHOULD NOT reveal information in | |||
| the cookie which identifies the server. This may be done by handling | the cookie which identifies the server. This may be done by handling | |||
| HelloRetryRequest statefully, thus not sending cookies, or by using | HelloRetryRequest statefully, thus not sending cookies, or by using | |||
| the same cookie construction for all backend servers. | the same cookie construction for all backend servers. | |||
| Note that, if the cookie includes a key name, analogous to Section 4 | Note that, if the cookie includes a key name, analogous to Section 4 | |||
| of [RFC5077], this may leak information if different backend servers | of [RFC5077], this may leak information if different backend servers | |||
| issue cookies with different key names at the time of the connection. | issue cookies with different key names at the time of the connection. | |||
| In particular, if the deployment operates in Split Mode, the backend | In particular, if the deployment operates in split mode, the backend | |||
| servers may not share cookie encryption keys. Backend servers may | servers may not share cookie encryption keys. Backend servers may | |||
| mitigate this either by handling key rotation with trial decryption | mitigate this either by handling key rotation with trial decryption | |||
| or by coordinating to match key names. | or by coordinating to match key names. | |||
| 10.9. Attacks Exploiting Acceptance Confirmation | 10.9. Attacks Exploiting Acceptance Confirmation | |||
| To signal acceptance, the backend server overwrites 8 bytes of its | To signal acceptance, the backend server overwrites 8 bytes of its | |||
| ServerHello.random with a value derived from the | ServerHello.random with a value derived from the | |||
| ClientHelloInner.random. (See Section 7.2 for details.) This | ClientHelloInner.random. (See Section 7.2 for details.) This | |||
| behavior increases the likelihood of the ServerHello.random colliding | behavior increases the likelihood of the ServerHello.random colliding | |||
| skipping to change at line 1859 ¶ | skipping to change at line 1843 ¶ | |||
| 10.10.5. Maintain Forward Secrecy | 10.10.5. Maintain Forward Secrecy | |||
| This design does not provide forward secrecy for the inner | This design does not provide forward secrecy for the inner | |||
| ClientHello because the server's ECH key is static. However, the | ClientHello because the server's ECH key is static. However, the | |||
| window of exposure is bound by the key lifetime. It is RECOMMENDED | window of exposure is bound by the key lifetime. It is RECOMMENDED | |||
| that servers rotate keys regularly. | that servers rotate keys regularly. | |||
| 10.10.6. Enable Multi-party Security Contexts | 10.10.6. Enable Multi-party Security Contexts | |||
| This design permits servers operating in Split Mode to forward | This design permits servers operating in split mode to forward | |||
| connections directly to backend origin servers. The client | connections directly to backend origin servers. The client | |||
| authenticates the identity of the backend origin server, thereby | authenticates the identity of the backend origin server, thereby | |||
| allowing the backend origin server to hide behind the client-facing | allowing the backend origin server to hide behind the client-facing | |||
| server without the client-facing server decrypting and reencrypting | server without the client-facing server decrypting and reencrypting | |||
| the connection. | the connection. | |||
| Conversely, if the DNS records used for configuration are | Conversely, if the DNS records used for configuration are | |||
| authenticated, e.g., via DNSSEC, spoofing a client-facing server | authenticated, e.g., via DNSSEC, spoofing a client-facing server | |||
| operating in Split Mode is not possible. See Section 10.2 for more | operating in split mode is not possible. See Section 10.2 for more | |||
| details regarding plaintext DNS. | details regarding plaintext DNS. | |||
| Authenticating the ECHConfig structure naturally authenticates the | Authenticating the ECHConfig structure naturally authenticates the | |||
| included public name. This also authenticates any retry signals from | included public name. This also authenticates any retry signals from | |||
| the client-facing server because the client validates the server | the client-facing server because the client validates the server | |||
| certificate against the public name before retrying. | certificate against the public name before retrying. | |||
| 10.10.7. Support Multiple Protocols | 10.10.7. Support Multiple Protocols | |||
| This design has no impact on application layer protocol negotiation. | This design has no impact on application layer protocol negotiation. | |||
| skipping to change at line 1919 ¶ | skipping to change at line 1903 ¶ | |||
| the attacker learns that its test certificate name was incorrect. As | the attacker learns that its test certificate name was incorrect. As | |||
| an example, suppose the client's SNI value in its inner ClientHello | an example, suppose the client's SNI value in its inner ClientHello | |||
| is "example.com," and the attacker replied with a Certificate for | is "example.com," and the attacker replied with a Certificate for | |||
| "test.com". If the client produces a verification failure alert | "test.com". If the client produces a verification failure alert | |||
| because of the mismatch faster than it would due to the Certificate | because of the mismatch faster than it would due to the Certificate | |||
| signature validation, information about the name leaks. Note that | signature validation, information about the name leaks. Note that | |||
| the attacker can also withhold the CertificateVerify message. In | the attacker can also withhold the CertificateVerify message. In | |||
| that scenario, a client which first verifies the Certificate would | that scenario, a client which first verifies the Certificate would | |||
| then respond similarly and leak the same information. | then respond similarly and leak the same information. | |||
| Client Attacker Server | ~~ Client Attacker Server ClientHello + key_share + ech ------> | |||
| ClientHello | (intercept) -----> X (drop) | |||
| + key_share | ||||
| + ech ------> (intercept) -----> X (drop) | ||||
| ServerHello | ServerHello | |||
| + key_share | + key_share | |||
| {EncryptedExtensions} | {EncryptedExtensions} | |||
| {CertificateRequest*} | {CertificateRequest*} | |||
| {Certificate*} | {Certificate*} | |||
| {CertificateVerify*} | {CertificateVerify*} | |||
| <------ | <------ Alert | |||
| Alert | ------> ~~ | |||
| ------> | ||||
| Figure 3: Client Reaction Attack | Figure 3: Client Reaction Attack | |||
| ClientHelloInner.random prevents this attack. In particular, since | ClientHelloInner.random prevents this attack. In particular, since | |||
| the attacker does not have access to this value, it cannot produce | the attacker does not have access to this value, it cannot produce | |||
| the right transcript and handshake keys needed for encrypting the | the right transcript and handshake keys needed for encrypting the | |||
| Certificate message. Thus, the client will fail to decrypt the | Certificate message. Thus, the client will fail to decrypt the | |||
| Certificate and abort the connection. | Certificate and abort the connection. | |||
| 10.12.2. HelloRetryRequest Hijack Mitigation | 10.12.2. HelloRetryRequest Hijack Mitigation | |||
| skipping to change at line 1958 ¶ | skipping to change at line 1939 ¶ | |||
| (ech) extension to the server, which triggers a legitimate | (ech) extension to the server, which triggers a legitimate | |||
| HelloRetryRequest in return. Rather than forward the retry to the | HelloRetryRequest in return. Rather than forward the retry to the | |||
| client, the attacker attempts to generate its own ClientHello in | client, the attacker attempts to generate its own ClientHello in | |||
| response based on the contents of the first ClientHello and | response based on the contents of the first ClientHello and | |||
| HelloRetryRequest exchange with the result that the server encrypts | HelloRetryRequest exchange with the result that the server encrypts | |||
| the Certificate to the attacker. If the server used the SNI from the | the Certificate to the attacker. If the server used the SNI from the | |||
| first ClientHello and the key share from the second (attacker- | first ClientHello and the key share from the second (attacker- | |||
| controlled) ClientHello, the Certificate produced would leak the | 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 + key_share + ech ------> | |||
| ClientHello | (forward) -------> HelloRetryRequest + key_share (intercept) <------- | |||
| + key_share | ||||
| + ech ------> (forward) -------> | ||||
| HelloRetryRequest | ||||
| + key_share | ||||
| (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) ~~ | |||
| Figure 4: HelloRetryRequest Hijack Attack | Figure 4: HelloRetryRequest Hijack Attack | |||
| This attack is mitigated by using the same HPKE context for both | This attack is mitigated by using the same HPKE context for both | |||
| ClientHello messages. The attacker does not possess the context's | ClientHello messages. The attacker does not possess the context's | |||
| keys, so it cannot generate a valid encryption of the second inner | keys, so it cannot generate a valid encryption of the second inner | |||
| ClientHello. | ClientHello. | |||
| If the attacker could manipulate the second ClientHello, it might be | If the attacker could manipulate the second ClientHello, it might be | |||
| possible for the server to act as an oracle if it required parameters | possible for the server to act as an oracle if it required parameters | |||
| skipping to change at line 2015 ¶ | skipping to change at line 1991 ¶ | |||
| To begin, the attacker first interacts with a server to obtain a | To begin, the attacker first interacts with a server to obtain a | |||
| resumption ticket for a given test domain, such as "example.com". | resumption ticket for a given test domain, such as "example.com". | |||
| Later, upon receipt of a ClientHelloOuter, it modifies it such that | Later, upon receipt of a ClientHelloOuter, it modifies it such that | |||
| the server will process the resumption ticket with ClientHelloInner. | the server will process the resumption ticket with ClientHelloInner. | |||
| If the server only accepts resumption PSKs that match the server | If the server only accepts resumption PSKs that match the server | |||
| name, it will fail the PSK binder check with an alert when | name, it will fail the PSK binder check with an alert when | |||
| ClientHelloInner is for "example.com" but silently ignore the PSK and | ClientHelloInner is for "example.com" but silently ignore the PSK and | |||
| continue when ClientHelloInner is for any other name. This | continue when ClientHelloInner is for any other name. This | |||
| introduces an oracle for testing encrypted SNI values. | introduces an 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) | |||
| + pre_shared_key | + pre_shared_key | |||
| --------> | --------> | |||
| (intercept) | (intercept) | |||
| ClientHello | ClientHello | |||
| + key_share | + key_share | |||
| + 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 | |||
| <-------- | <-------- ~~ | |||
| Figure 5: Message Flow for Malleable ClientHello | Figure 5: Message Flow for Malleable ClientHello | |||
| This attack may be generalized to any parameter which the server | This attack may be generalized to any parameter which the server | |||
| varies by server name, such as ALPN preferences. | varies by 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 and authenticating all inputs to the | |||
| ClientHelloInner (EncodedClientHelloInner and ClientHelloOuter) with | ClientHelloInner (EncodedClientHelloInner and ClientHelloOuter) with | |||
| the HPKE AEAD. See Section 5.2. The decompression process in | the HPKE AEAD. See Section 5.2. The decompression process in | |||
| skipping to change at line 2074 ¶ | skipping to change at line 2050 ¶ | |||
| the number of extensions, the overall decoding process would take | the number of extensions, the overall decoding process would take | |||
| O(M*N) time, where M is the number of extensions in | O(M*N) time, where M is the number of extensions in | |||
| ClientHelloOuter and N is the size of OuterExtensions. | ClientHelloOuter and N is the size of OuterExtensions. | |||
| * If the same ClientHelloOuter extension can be copied multiple | * If the same ClientHelloOuter extension can be copied multiple | |||
| times, an attacker could cause the client-facing server to | times, an attacker could cause the client-facing server to | |||
| construct a large ClientHelloInner by including a large extension | construct a large ClientHelloInner by including a large extension | |||
| in ClientHelloOuter of length L and an OuterExtensions list | in ClientHelloOuter of length L and an OuterExtensions list | |||
| referencing N copies of that extension. The client-facing server | referencing N copies of that extension. The client-facing server | |||
| would then use O(N*L) memory in response to O(N+L) bandwidth from | would then use O(N*L) memory in response to O(N+L) bandwidth from | |||
| the client. In split-mode, an O(N*L)-sized packet would then be | the client. In split mode, an O(N*L)-sized packet would then be | |||
| transmitted to the backend server. | transmitted to the backend server. | |||
| ECH mitigates this attack by requiring that OuterExtensions be | ECH mitigates this attack by requiring that OuterExtensions be | |||
| referenced in order, that duplicate references be rejected, and by | referenced in order, that duplicate references be rejected, and by | |||
| recommending that client-facing servers use a linear scan to perform | recommending that client-facing servers use a linear scan to perform | |||
| decompression. These requirements are detailed in Section 5.1. | decompression. These requirements are detailed in Section 5.1. | |||
| 11. IANA Considerations | 11. IANA Considerations | |||
| 11.1. Update of the TLS ExtensionType Registry | 11.1. Update of the TLS ExtensionType Registry | |||
| skipping to change at line 2113 ¶ | skipping to change at line 2089 ¶ | |||
| 11.3. ECH Configuration Extension Registry | 11.3. ECH Configuration Extension Registry | |||
| IANA has created a new "TLS ECHConfig Extension" registry in a new | IANA has created a new "TLS ECHConfig Extension" registry in a new | |||
| "TLS Encrypted Client Hello (ECH) Configuration Extensions" registry | "TLS Encrypted Client Hello (ECH) Configuration Extensions" registry | |||
| group. New registrations will list the following attributes: | group. New registrations will list the following attributes: | |||
| Value: The two-byte identifier for the ECHConfigExtension, i.e., the | Value: The two-byte identifier for the ECHConfigExtension, i.e., the | |||
| ECHConfigExtensionType | ECHConfigExtensionType | |||
| Extension Name: Name of the ECHConfigExtension | Extension Name: Name of the ECHConfigExtension | |||
| Recommended: A "Y" or "N" value indicating if the extension is TLS | Recommended: A "Y" or "N" value indicating if the TLS Working Group | |||
| WG recommends that the extension be supported. This column is | recommends that the extension be supported. This column is | |||
| assigned a value of "N" unless explicitly requested. Adding a | assigned a value of "N" unless explicitly requested. Adding a | |||
| value with a value of "Y" requires Standards Action [RFC8126]. | value of "Y" requires Standards Action [RFC8126]. | |||
| Reference: The specification where the ECHConfigExtension is defined | Reference: The specification where the ECHConfigExtension is defined | |||
| Notes: Any notes associated with the entry | Notes: Any notes associated with the entry | |||
| New entries in the "TLS ECHConfig Extension" registry are subject to | New entries in the "TLS ECHConfig Extension" registry are subject to | |||
| the Specification Required registration policy ([RFC8126], | the Specification Required registration policy ([RFC8126], | |||
| Section 4.6), with the policies described in [RFC8447], Section 17. | Section 4.6), with the policies described in [RFC8447], Section 17. | |||
| IANA has added the following note to the "TLS ECHConfig Extension" | IANA has added the following note to the "TLS ECHConfig Extension" | |||
| registry: | registry: | |||
| Note: The role of the designated expert is described in RFC 8447. | Note: The role of the designated expert is described in RFC 8447. | |||
| skipping to change at line 2147 ¶ | skipping to change at line 2123 ¶ | |||
| The initial contents for this registry consists of multiple reserved | The initial contents for this registry consists of multiple reserved | |||
| values with the following attributes, which are repeated for each | values with the following attributes, which are repeated for each | |||
| registration: | registration: | |||
| Value: 0x0000, 0x1A1A, 0x2A2A, 0x3A3A, 0x4A4A, 0x5A5A, 0x6A6A, | Value: 0x0000, 0x1A1A, 0x2A2A, 0x3A3A, 0x4A4A, 0x5A5A, 0x6A6A, | |||
| 0x7A7A, 0x8A8A, 0x9A9A, 0xAAAA, 0xBABA, 0xCACA, 0xDADA, 0xEAEA, | 0x7A7A, 0x8A8A, 0x9A9A, 0xAAAA, 0xBABA, 0xCACA, 0xDADA, 0xEAEA, | |||
| 0xFAFA | 0xFAFA | |||
| Extension Name: RESERVED | Extension Name: RESERVED | |||
| Recommended: Y | Recommended: Y | |||
| Reference: RFC 9849 | Reference: RFC 9849 | |||
| Notes: Grease entries | Notes: GREASE entries | |||
| 12. References | 12. References | |||
| 12.1. Normative References | 12.1. Normative References | |||
| [HPKE] Barnes, R., Bhargavan, K., Lipp, B., and C. Wood, "Hybrid | [HPKE] Barnes, R., Bhargavan, K., Lipp, B., and C. Wood, "Hybrid | |||
| Public Key Encryption", RFC 9180, DOI 10.17487/RFC9180, | Public Key Encryption", RFC 9180, DOI 10.17487/RFC9180, | |||
| February 2022, <https://www.rfc-editor.org/info/rfc9180>. | February 2022, <https://www.rfc-editor.org/info/rfc9180>. | |||
| [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate | [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate | |||
| Requirement Levels", BCP 14, RFC 2119, | Requirement Levels", BCP 14, RFC 2119, | |||
| DOI 10.17487/RFC2119, March 1997, | DOI 10.17487/RFC2119, March 1997, | |||
| <https://www.rfc-editor.org/info/rfc2119>. | <https://www.rfc-editor.org/info/rfc2119>. | |||
| [RFC5890] Klensin, J., "Internationalized Domain Names for | [RFC5890] Klensin, J., "Internationalized Domain Names for | |||
| Applications (IDNA): Definitions and Document Framework", | Applications (IDNA): Definitions and Document Framework", | |||
| RFC 5890, DOI 10.17487/RFC5890, August 2010, | RFC 5890, DOI 10.17487/RFC5890, August 2010, | |||
| <https://www.rfc-editor.org/info/rfc5890>. | <https://www.rfc-editor.org/info/rfc5890>. | |||
| [RFC6125] Saint-Andre, P. and J. Hodges, "Representation and | ||||
| Verification of Domain-Based Application Service Identity | ||||
| within Internet Public Key Infrastructure Using X.509 | ||||
| (PKIX) Certificates in the Context of Transport Layer | ||||
| Security (TLS)", RFC 6125, DOI 10.17487/RFC6125, March | ||||
| 2011, <https://www.rfc-editor.org/info/rfc6125>. | ||||
| [RFC7918] Langley, A., Modadugu, N., and B. Moeller, "Transport | [RFC7918] Langley, A., Modadugu, N., and B. Moeller, "Transport | |||
| Layer Security (TLS) False Start", RFC 7918, | Layer Security (TLS) False Start", RFC 7918, | |||
| DOI 10.17487/RFC7918, August 2016, | DOI 10.17487/RFC7918, August 2016, | |||
| <https://www.rfc-editor.org/info/rfc7918>. | <https://www.rfc-editor.org/info/rfc7918>. | |||
| [RFC8126] Cotton, M., Leiba, B., and T. Narten, "Guidelines for | [RFC8126] Cotton, M., Leiba, B., and T. Narten, "Guidelines for | |||
| Writing an IANA Considerations Section in RFCs", BCP 26, | Writing an IANA Considerations Section in RFCs", BCP 26, | |||
| RFC 8126, DOI 10.17487/RFC8126, June 2017, | RFC 8126, DOI 10.17487/RFC8126, June 2017, | |||
| <https://www.rfc-editor.org/info/rfc8126>. | <https://www.rfc-editor.org/info/rfc8126>. | |||
| skipping to change at line 2206 ¶ | skipping to change at line 2175 ¶ | |||
| [RFC9147] Rescorla, E., Tschofenig, H., and N. Modadugu, "The | [RFC9147] Rescorla, E., Tschofenig, H., and N. Modadugu, "The | |||
| Datagram Transport Layer Security (DTLS) Protocol Version | Datagram Transport Layer Security (DTLS) Protocol Version | |||
| 1.3", RFC 9147, DOI 10.17487/RFC9147, April 2022, | 1.3", RFC 9147, DOI 10.17487/RFC9147, April 2022, | |||
| <https://www.rfc-editor.org/info/rfc9147>. | <https://www.rfc-editor.org/info/rfc9147>. | |||
| [RFC9460] Schwartz, B., Bishop, M., and E. Nygren, "Service Binding | [RFC9460] Schwartz, B., Bishop, M., and E. Nygren, "Service Binding | |||
| and Parameter Specification via the DNS (SVCB and HTTPS | and Parameter Specification via the DNS (SVCB and HTTPS | |||
| Resource Records)", RFC 9460, DOI 10.17487/RFC9460, | Resource Records)", RFC 9460, DOI 10.17487/RFC9460, | |||
| November 2023, <https://www.rfc-editor.org/info/rfc9460>. | November 2023, <https://www.rfc-editor.org/info/rfc9460>. | |||
| [RFCYYY1] Schwartz, B., Bishop, M., and E. Nygren, "Bootstrapping | [RFC9525] Saint-Andre, P. and R. Salz, "Service Identity in TLS", | |||
| TLS Encrypted ClientHello with DNS Service Bindings", | RFC 9525, DOI 10.17487/RFC9525, November 2023, | |||
| RFC YYY1, DOI 10.17487/RFCYYY1, November 2025, | <https://www.rfc-editor.org/info/rfc9525>. | |||
| <https://www.rfc-editor.org/info/rfcYYY1>. | ||||
| 12.2. Informative References | 12.2. Informative References | |||
| [DNS-TERMS] | [DNS-TERMS] | |||
| Hoffman, P. and K. Fujiwara, "DNS Terminology", BCP 219, | Hoffman, P. and K. Fujiwara, "DNS Terminology", BCP 219, | |||
| RFC 9499, DOI 10.17487/RFC9499, March 2024, | RFC 9499, DOI 10.17487/RFC9499, March 2024, | |||
| <https://www.rfc-editor.org/info/rfc9499>. | <https://www.rfc-editor.org/info/rfc9499>. | |||
| [ECH-Analysis] | [ECH-Analysis] | |||
| Bhargavan, K., Cheval, V., and C. Wood, "A Symbolic | Bhargavan, K., Cheval, V., and C. Wood, "A Symbolic | |||
| skipping to change at line 2287 ¶ | skipping to change at line 2255 ¶ | |||
| [RFC8744] Huitema, C., "Issues and Requirements for Server Name | [RFC8744] Huitema, C., "Issues and Requirements for Server Name | |||
| Identification (SNI) Encryption in TLS", RFC 8744, | Identification (SNI) Encryption in TLS", RFC 8744, | |||
| DOI 10.17487/RFC8744, July 2020, | DOI 10.17487/RFC8744, July 2020, | |||
| <https://www.rfc-editor.org/info/rfc8744>. | <https://www.rfc-editor.org/info/rfc8744>. | |||
| [RFC9250] Huitema, C., Dickinson, S., and A. Mankin, "DNS over | [RFC9250] Huitema, C., Dickinson, S., and A. Mankin, "DNS over | |||
| Dedicated QUIC Connections", RFC 9250, | Dedicated QUIC Connections", RFC 9250, | |||
| DOI 10.17487/RFC9250, May 2022, | DOI 10.17487/RFC9250, May 2022, | |||
| <https://www.rfc-editor.org/info/rfc9250>. | <https://www.rfc-editor.org/info/rfc9250>. | |||
| [RFCYYY1] Schwartz, B., Bishop, M., and E. Nygren, "Bootstrapping | ||||
| TLS Encrypted ClientHello with DNS Service Bindings", | ||||
| RFC YYY1, DOI 10.17487/RFCYYY1, December 2025, | ||||
| <https://www.rfc-editor.org/info/rfcYYY1>. | ||||
| [WHATWG-IPV4] | [WHATWG-IPV4] | |||
| WHATWG, "URL - IPv4 Parser", WHATWG Living Standard, May | WHATWG, "URL - IPv4 Parser", WHATWG Living Standard, May | |||
| 2021, <https://url.spec.whatwg.org/#concept-ipv4-parser>. | 2021, <https://url.spec.whatwg.org/#concept-ipv4-parser>. | |||
| Appendix A. Linear-Time Outer Extension Processing | Appendix A. Linear-Time Outer Extension Processing | |||
| The following procedure processes the "ech_outer_extensions" | The following procedure processes the "ech_outer_extensions" | |||
| extension (see Section 5.1) in linear time, ensuring that each | extension (see Section 5.1) in linear time, ensuring that each | |||
| referenced extension in the ClientHelloOuter is included at most | referenced extension in the ClientHelloOuter is included at most | |||
| once: | once: | |||
| skipping to change at line 2338 ¶ | skipping to change at line 2311 ¶ | |||
| Kazuho Oku | Kazuho Oku | |||
| Fastly | Fastly | |||
| Email: kazuhooku@gmail.com | Email: kazuhooku@gmail.com | |||
| Nick Sullivan | Nick Sullivan | |||
| Cryptography Consulting LLC | Cryptography Consulting LLC | |||
| Email: nicholas.sullivan+ietf@gmail.com | Email: nicholas.sullivan+ietf@gmail.com | |||
| Christopher A. Wood | Christopher A. Wood | |||
| Cloudflare | Apple | |||
| Email: caw@heapingbits.net | Email: caw@heapingbits.net | |||
| End of changes. 48 change blocks. | ||||
| 144 lines changed or deleted | 117 lines changed or added | |||
This html diff was produced by rfcdiff 1.48. | ||||