jiebi/TextFlow huggingface dataset:
Original text:
When the client-facing server accepts ECH, it forwards the ClientHelloInner to the backend server, who terminates the <del> connection.  If the ClientHelloInner contains an empty "encrypted_client_hello" extension, then the backend server MUST confirm ECH acceptance by setting ServerHello.random[24:32] to </del>  where HKDF-Expand-Label and HKDF-Extract are as defined in RFC8446. The value of ServerHello.random[0:24] is generated as usual by invoking a secure random number generator (see RFC8446, Section 4.1.2).  8. Unlike most TLS extensions, placing the SNI value in an ECH extension Note that the same bytes of the ServerHello.random are used to implement downgrade protection for TLS 1.3 (see RFC8446, <del> Section 4.1.3).  The backend server's signal of acceptance does not interfere with this mechanism because ECH is only supported in TLS 1.3 or higher. </del>  10.8.

Reviewer comments:
As written, the backend server performs this URL modification unconditionally when it sees an empty ECH extension. Although the client is required to only offer TLS 1.3, the backend server doesn't check this. However, as noted elsewhere in the document, these same 8 bytes are used for the TLS 1.3 downgrade protection mechanism. That mechanism only works if the server never produces a TLS 1.2 ServerKeyExchange signature without the signal, so it's important that the server not signal ECH acceptance. Write this down.
Oops. Good catch!Yup, this is a good idea.

Revised text:
When the client-facing server accepts ECH, it forwards the ClientHelloInner to the backend server, who terminates the  <ins> connection.  If the backend server negotiates TLS 1.3 or higher and the ClientHelloInner contains an empty "encrypted_client_hello" extension, then the backend server MUST confirm ECH acceptance by setting ServerHello.random[24:32] to </ins> where HKDF-Expand-Label and HKDF-Extract are as defined in RFC8446. The value of ServerHello.random[0:24] is generated as usual by invoking a secure random number generator (see RFC8446, Section 4.1.2). <ins> 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 RFC8446, Section 4.1.3). </ins> 8. Unlike most TLS extensions, placing the SNI value in an ECH extension Note that the same bytes of the ServerHello.random are used to implement downgrade protection for TLS 1.3 (see RFC8446,  <ins> Section 4.1.3).  These mechanisms do not interfere because the backend server only signals ECH acceptance in TLS 1.3 or higher. </ins> 10.8.
Original text:
4. <del> ECH uses draft-05 of HPKE for public key encryption I-D.irtf-cfrg- </del>  hpke.  The ECH configuration is defined by the following "ECHConfig" structure.

Reviewer comments:
ESNI seems to rely on TLS 1.3 KeyShareEntry which makes it incompatible with TLS 1.2. That's unfortunate with that "The protocol designed in this document is quite straightforward." The gist of ESNI comes down to "encryptedservername extension, which contains the true extension encrypted under the provider’s public key." That seems pretty generic and backwards compatible.
To prevent cut-and-paste attacks, you need to bind ESNI to the CH. That means we’d need 1.2 clients to send a key share in the CH to use when deriving encryption keying material, and that’s not currently done.
Since TLS 1.2 sends the server certificate in plaintext it seems like ESNI wouldn't be particularly useful there anyway, no?
Indeed. I guess someone could argue about wildcard certificates or when multiple names are present but the names are usually related so I agree there’s no significant benefit from encrypting the SNI.

Revised text:
4.  <ins> ECH uses draft-06 of HPKE for public key encryption I-D.irtf-cfrg- </ins> hpke.  The ECH configuration is defined by the following "ECHConfig" structure.
Original text:
7. <del> Servers that support ECH play one of two roles, depending on the extension in the ClientHello.  If the "encrypted_client_hello" extension is present, the server acts as a client-facing server and proceeds as described in client-facing-server to extract a ClientHelloInner, if available.  If the "ech_is_inner" extension is present and the "encrypted_client_hello" extension is not present, the server acts as a backend server and proceeds as described in backend-server. </del>  7.1. Upon receipt of an "ech_is_inner" extension in a ClientHello, if the backend server negotiates TLS 1.3 or higher, then it MUST confirm ECH acceptance to the client by computing its ServerHello as described <del> here.  If both the "ech_is_inner" and "encrypted_client_hello" extensions are present in the ClientHello, the backend server MUST abort with an "illegal_parameter" alert. </del>  The backend server begins by generating a message ServerHelloECHConf, which is identical in content to a ServerHello message with the

Reviewer comments:
The text for servers rejecting both extensions present is under Backend Server, but the Server Behavior text gates that section under just the one extension. Likewise, the client-facing server text allows for both extensions to be present. Move the illegal_parameter text around and write out the four cases.

Revised text:
7.  <ins> Servers that support ECH play one of two roles, depending on which of the "ech_is_inner" and "encrypted_client_hello" extensions are present in the ClientHello: If both the "ech_is_inner" and "encrypted_client_hello" extensions are present in the ClientHello, the backend server MUST abort with an "illegal_parameter" alert. If only the "encrypted_client_hello" extension is present, the server acts as a client-facing server and proceeds as described in client-facing-server to extract a ClientHelloInner, if available. If only the "ech_is_inner" extension is present and the "encrypted_client_hello" extension is not present, the server acts as a backend server and proceeds as described in backend-server. If neither extension is present, the server completes the handshake normally, as described in RFC8446. </ins> 7.1. Upon receipt of an "ech_is_inner" extension in a ClientHello, if the backend server negotiates TLS 1.3 or higher, then it MUST confirm ECH acceptance to the client by computing its ServerHello as described  <ins> here. </ins> The backend server begins by generating a message ServerHelloECHConf, which is identical in content to a ServerHello message with the
Original text:
config), "tls ech config id", 8)", unless it is optional for an application; see optional-configs. "config" is the "ECHConfig" structure.  "Extract" and "Expand" are as specified by the cipher <del> suite KDF.  (Passing the literal """" as the salt is interpreted by "Extract" as no salt being provided.) </del>  The HPKE encapsulated key, used by servers to decrypt the corresponding "payload" field.

Reviewer comments:
The backtick-quoted empty string translates to """" in the text output.
Not sure if this is the best solution, but the quadruple quote in the txt is goofy.
This seems fine, yeah. Thanks for catching it!

Revised text:
config), "tls ech config id", 8)", unless it is optional for an application; see optional-configs. "config" is the "ECHConfig" structure.  "Extract" and "Expand" are as specified by the cipher  <ins> suite KDF.  (Passing the literal "" as the salt is interpreted by "Extract" as no salt being provided.) </ins> The HPKE encapsulated key, used by servers to decrypt the corresponding "payload" field.
Original text:
TLS 1.2 or below.  Note that doing so would overwrite the downgrade signal for TLS 1.3 (see RFC8446, Section 4.1.3).  8. Unlike most TLS extensions, placing the SNI value in an ECH extension

Reviewer comments:
~It's sufficient to ignore it, I think.~ The backend server enforces the extension's emptiness.

Revised text:
TLS 1.2 or below.  Note that doing so would overwrite the downgrade signal for TLS 1.3 (see RFC8446, Section 4.1.3). <ins> The "ech_is_inner" is expected to have an empty payload.  If the payload is non-empty (i.e., the length of the "extension_data" field is non-zero) then the backend server MUST abort the handshake with an "illegal_parameter" alert. </ins> 8. Unlike most TLS extensions, placing the SNI value in an ECH extension
Original text:
fields of the payload of the "encrypted_client_hello" extension.  The last parameter, "outer_hello", is computed by serializing ClientHelloOuter with the "encrypted_client_hello" extension removed.  Note the decompression process in encoding-inner forbids "encrypted_client_hello" in OuterExtensions.  This ensures the

Reviewer comments:
Clarifies that serialization of this structure doesn't include the message type and length. We do the same thing for the serialization of EncodedClientHelloInner, but this makes it explicit in case it's not clear from context.

Revised text:
fields of the payload of the "encrypted_client_hello" extension.  The last parameter, "outer_hello", is computed by serializing ClientHelloOuter with the "encrypted_client_hello" extension removed. <ins> Note this does not include the four-byte header included in the Handshake structure. </ins> Note the decompression process in encoding-inner forbids "encrypted_client_hello" in OuterExtensions.  This ensures the
Original text:
An ECHConfigs structure containing one or more ECHConfig structures, in decreasing order of preference, to be used by the <del> client in subsequent connection attempts. </del>  This document also defines the "ech_required" alert, which the client MUST send when it offered an "encrypted_client_hello" extension that handshake, authenticating for ECHConfig.contents.public_name as described in auth-public-name.  If authentication or the handshake fails, the client MUST return a failure to the calling application. <del> It MUST NOT use the retry keys. </del>  <del> Otherwise, when the handshake completes successfully with the public name authenticated, the client MUST abort the connection with an </del>  "ech_required" alert.  It then processes the "retry_configs" field from the server's "encrypted_client_hello" extension. The client MUST continue to use the previously-advertised configurations for subsequent connections.  This avoids introducing pinning concerns or a tracking vector, should a malicious server <del> present client-specific retry keys in order to identify the client in a subsequent ECH handshake. </del>  If none of the values provided in "retry_configs" contains a supported version, the client can regard ECH as securely disabled by

Reviewer comments:
The reference to "retry keys" comes from when the field was called retrykeys and the type ESNIKeys. We've since renamed them to retryconfig and ECHConfig, so the prose should match.

Revised text:
An ECHConfigs structure containing one or more ECHConfig structures, in decreasing order of preference, to be used by the  <ins> client in subsequent connection attempts.  These are known as the server's "retry configurations". </ins> This document also defines the "ech_required" alert, which the client MUST send when it offered an "encrypted_client_hello" extension that handshake, authenticating for ECHConfig.contents.public_name as described in auth-public-name.  If authentication or the handshake fails, the client MUST return a failure to the calling application.  <ins> It MUST NOT use the retry configurations. </ins>  <ins> Otherwise, if both authentication and the handshake complete successfully, the client MUST abort the connection with an </ins> "ech_required" alert.  It then processes the "retry_configs" field from the server's "encrypted_client_hello" extension. The client MUST continue to use the previously-advertised configurations for subsequent connections.  This avoids introducing pinning concerns or a tracking vector, should a malicious server  <ins> present client-specific retry configurations in order to identify the client in a subsequent ECH handshake. </ins> If none of the values provided in "retry_configs" contains a supported version, the client can regard ECH as securely disabled by
Original text:
4. <del> ECH uses draft-07 of HPKE for public key encryption I-D.irtf-cfrg- </del>  hpke.  The ECH configuration is defined by the following "ECHConfig" structure. IANA is requested to create the following three entries in the existing registry for ExtensionType (defined in RFC8446): <del> encrypted_client_hello(0xfe09), with "TLS 1.3" column values set </del>  to "CH, EE", and "Recommended" column set to "Yes". ech_is_inner (0xda09), with "TLS 1.3" column values set to "CH",

Reviewer comments:
and also updates the ECH version. I didn't bump the extension codepoint for "echisinner," since that seems harmless. cc NAME NAME
This new version of the draft removes the version-vector dependency, so this should be the last time we update the pinned version.
Works for me!

Revised text:
4.  <ins> ECH uses draft-08 of HPKE for public key encryption I-D.irtf-cfrg- </ins> hpke.  The ECH configuration is defined by the following "ECHConfig" structure. IANA is requested to create the following three entries in the existing registry for ExtensionType (defined in RFC8446):  <ins> encrypted_client_hello(0xfe0a), with "TLS 1.3" column values set </ins> to "CH, EE", and "Recommended" column set to "Yes". ech_is_inner (0xda09), with "TLS 1.3" column values set to "CH",
Original text:
not decrypt the second ClientHello's ClientECH.payload value, if there is one. <del> [[OPEN ISSUE: If the client-facing server implements stateless HRR, it has no way to send a cookie, short of as-yet-unspecified integration with the backend server.  Stateful HRR on the client- facing server works fine, however.  See issue https://github.com/tlswg/draft-ietf-tls-esni/issues/333.]] </del>  7.2.

Reviewer comments:
URL don't give both servers a cookie. That is, if a client-facing server forwards the ClientHello, it can't do so statelessly without coordinating with the backend. This is a consequence of moving to a single HelloRetryRequest, but it's a minor one. In most cases, there will be state established anyway just to get the connection forwarded. DTLS might not work that way, but it's still likely reasonable to assume state. No longer builds on .
I guess the dust is still settling on the rest of the HRR mess, but a6b6b67ca710d023962b8b950a2d26f2806d7aac LGTM.
+1 -- NAME can we cherry-pick a6b6b67 into a separate PR?
I would be happy for you to take that and merge it.
Or, I will just rebase -i here.
I was thinking about and the discussion on server HRR checks started by URL, and I'm not sure HRR cookies even work in split mode right now. First off, suppose we don't merge something like . The current text says: This means if you get a cookied HRR only good for ClientHelloInner, there's no requirement to stick the cookie in ClientHelloOuter! But now the client-facing server can't see the cookie, so it can't be stateless. It would seem like fixes this, but now we have a more fundamental problem: whose cookie? Suppose the client-facing server wishes to handle HRR statelessly. It decrypts the first ClientHelloInner and forwards it to the backend server. If the backend server responds with HRR, the client-facing server must forward the HRR as-is, or the transcript breaks. But the client-facing server has no opportunity to bind its state (echhrrkey) into the HRR cookie. (Perhaps there isn't even one. If there is, it's encrypted with the backend server's key anyway. Either way, it's fixed now.) The simplest solution to all this is to only support stateful client-facing servers in split mode, unless the client-facing and backend servers can coordinate enough to share one cookie. I don't know if that would satisfy everyone. I could imagine doing something horrific to URL, akin to what we did to URL, but that sounds really messy.
Let's mark this as an "OPEN ISSUE" in the draft.
Done. URL

Revised text:
not decrypt the second ClientHello's ClientECH.payload value, if there is one.  <ins> Note that a client-facing server that forwards the first ClientHello cannot include its own "cookie" extension if the backend server sends a HelloRetryRequest.  This means that the client-facing server either needs to maintain state for such a connection or it needs to coordinate with the backend server to include any information it requires to process the second ClientHello. </ins> 7.2.
Original text:
last parameter, "outer_hello", is computed by serializing ClientHelloOuter with the "encrypted_client_hello" extension set to the empty string, i.e., the "extension_data" list has zero length. <del> Note this serialization does not include the four-byte header included in the Handshake structure. </del>  The decompression process in encoding-inner forbids "encrypted_client_hello" in OuterExtensions.  This ensures the

Reviewer comments:
ClientHello is already a defined structure. No need to wrap it in an opaque byte array. This avoids needing to fill in an extra length prefix in front.
Isn't the length prefix there in order to prevent ossification?
Could you elaborate? I don't see how this relates to ossification. Note AAD values aren't parsed or even sent over the wire. Rather, the requirement on them is injectivity.
If the thinking is one might want to change what goes in the AAD later, the length prefix doesn't do anything. You'd need an actual distinguishing field like a type or version. We get that via labels and such when setting up the ECH context itself. By the time we've settled on speaking a particular ECH-like protocol, the AAD structure is fixed.
Thanks for the clarification. I was confused about the specifics of how this prefix was used.
Can probably just say instead of leading with , which is almost always unnecessary.

Revised text:
last parameter, "outer_hello", is computed by serializing ClientHelloOuter with the "encrypted_client_hello" extension set to the empty string, i.e., the "extension_data" list has zero length.  <ins> This serialization uses the ClientHello structure from Section 4.1.2 of RFC8446, which does not include the four-byte header included in the Handshake structure. </ins> The decompression process in encoding-inner forbids "encrypted_client_hello" in OuterExtensions.  This ensures the
Original text:
the query domain name.  That is, if a client queries example.com, the ESNI TXT Resource Record might be: <del> Servers MUST ensure that, if multiple A, AAAA or ALTSVC I-D.schwartz- httpbis-dns-alt-svc records are returned for a domain with ESNI support, all the servers pointed to by those records are able to handle the keys returned as part of a ESNI TXT record for that domain. </del>  Alt-Svc records may be used to inform the client of the plaintext (fronting) SNI.  If present, clients SHOULD use its value in the SNI

Reviewer comments:
Also see URL
LGTM -- thanks!
I suggest dropping the references to Ben's individual alt-svc in DNS draft.. it hasn't been adopted and its future is murky. actual alt-svc (the non dns kind) actually requires the use of the plaintext SNI. As those are both cachable and connections to new IP addresses (less linkable) you could consider updating it with mike bishop's URL (also an individual draft atm)
NAME also suggested that the reference might not be needed in URL so I made to actually remove it.

Revised text:
the query domain name.  That is, if a client queries example.com, the ESNI TXT Resource Record might be:  <ins> Servers MUST ensure that, if multiple A or AAAA records are returned for a domain with ESNI support, all the servers pointed to by those records are able to handle the keys returned as part of a ESNI TXT record for that domain. </ins> Alt-Svc records may be used to inform the client of the plaintext (fronting) SNI.  If present, clients SHOULD use its value in the SNI
Original text:
"ECHConfigContents.cipher_suites" list. The HPKE encapsulated key, used by servers to decrypt the <del> corresponding "payload" field. </del>  The serialized and encrypted ClientHelloInner structure, encrypted using HPKE as described in real-ech.

Reviewer comments:
The second ClientHello after HelloRetryRequest reuses the HPKE context and calls Seal/Open a second time. To avoid needing to unnecessarily retain the enc value, and discourage servers from mistakenly recreating the HPKE context, we made it use the empty string but forgot to update the syntax to match.

Revised text:
"ECHConfigContents.cipher_suites" list. The HPKE encapsulated key, used by servers to decrypt the  <ins> corresponding "payload" field.  This field is empty in ClientHelloOuters sent in response to HelloRetryRequest. </ins> The serialized and encrypted ClientHelloInner structure, encrypted using HPKE as described in real-ech.
Original text:
that of any valid ECHConfig, which could include any ECHConfig that has been recently removed from active use. <del> The longest name of a backend server, if known.  If this value is not known it can be set to zero, in which case clients SHOULD use the inner ClientHello padding scheme described below.  That could happen if wildcard names are in use, or if names can be added or removed from the anonymity set during the lifetime of a particular ECH configuration. </del>  The non-empty name of the client-facing server, i.e., the entity trusted to update the ECH configuration.  This is used to correct

Reviewer comments:
While padding is "described below", it's pretty far away now. We also recommend the padding scheme whether or not the value is non-zero. The text about wildcard names also applies in both cases and seems to describe when the server name may exceed the max. This rephases things a bit.

Revised text:
that of any valid ECHConfig, which could include any ECHConfig that has been recently removed from active use.  <ins> The longest name of a backend server, if known.  If not known, this value can be set to zero.  It is used to compute padding (padding) and does not constrain server name lengths.  Names may exceed this length if, e.g., the server uses wildcard names or added new names to the anonymity set. </ins> The non-empty name of the client-facing server, i.e., the entity trusted to update the ECH configuration.  This is used to correct
Original text:
IANA is requested to create the following three entries in the existing registry for ExtensionType (defined in RFC8446): <del> encrypted_client_hello(0xfe0a), with "TLS 1.3" column values set </del>  to "CH, HRR, EE", and "Recommended" column set to "Yes". ech_outer_extensions(0xfd00), with the "TLS 1.3" column values set

Reviewer comments:
cc/ NAME
I don't think we need to also bump echouterextensions but just in case... S
ECHConfig is defined as but the text below it says The intention was to bump ECHConfig.version from 0xfe0a to 0xfe0b for draft-11, but this got missed by URL (I wrote the PR, so I apologize for my part in missing this.) How do folks think we should resolve this? Do we need to cut a new draft with the ambiguity fixed?
Oh whoops, nice catch! Using definitely won't work since the format and corresponding ClientECH extension has changed. I suppose all the implementors could just agree to mentally fix this typo when reading the draft, but may as well cut draft-12. Draft numbers are cheap.
We could try to pick up some editorial changes, too, like those suggested in .
I'd also personally be pretty happy to have in there, but also fine to skip it for what's supposed to be a small bugfix draft snapshot.
+1 I've not checked recent editorial changes but they're probably fine too. S
I'd be happy to take in draft-12 as well, though I'm inclined to think this should strictly be a bugfix/editorial draft. I'd say let's wait until Chris W. gets off back from PTO and let him make the call. Unless we're in a hurry, in which case we could ping one of the other authors to merge our changes and cut a draft.
Closing as resolved.

Revised text:
IANA is requested to create the following three entries in the existing registry for ExtensionType (defined in RFC8446):  <ins> encrypted_client_hello(0xfe0b), with "TLS 1.3" column values set </ins> to "CH, HRR, EE", and "Recommended" column set to "Yes". ech_outer_extensions(0xfd00), with the "TLS 1.3" column values set