jiebi/TextFlow huggingface dataset:
Original text:
enc contains the encapsulated key as output by SetupBaseS; and <del> encrypted_ch contains the HPKE encapsulated key (enc) and the ClientHelloInner ciphertext (encrypted_ch_inner). </del>  The client MUST place the value of "ECHConfig.public_name" in the ClientHelloOuter "server_name" extension.  See outer-clienthello for

Reviewer comments:
While reading the draft, I'm confused with ClientEncryptedCH structure. Section : The "encryptedclienthello" extension says... ~ >enc : The HPKE encapsulated key, used by servers to decrypt the corresponding field. >encryptedch : The serialized and encrypted ClientHelloInner structure, encrypted using HPKE as described in {{send-ech}}. and Section : Sending an encrypted ClientHello says... >The encrypted ClientHello value is then computed as: ~ Cause output of the context.Seal() function is ciphertext output of AEAD encryption (According to draft-irtf-cfrg-hpke-05), I think encryptedch has only ciphertext value of the ClientHelloInner, not the enc value. But the same section also says... >encryptedch contains the HPKE encapsulated key (enc) and the ClientHelloInner ciphertext (encryptedchinner). Q. Does encryptedch have enc and encryptedchinner? Or it just has the ciphertext of ClientHelloInner and encryptedch_inner is just mistype?
Nice catch! should just be the AEAD ciphertext. The following text is wrong: ciphertext (encryptedch_inner). It should be: cc/ NAME
OK, there's no .. thanks!

Revised text:
enc contains the encapsulated key as output by SetupBaseS; and  <ins> encrypted_ch contains the AEAD-encrypted ClientHelloInner. </ins> The client MUST place the value of "ECHConfig.public_name" in the ClientHelloOuter "server_name" extension.  See outer-clienthello for
Original text:
Per client-behavior and server-behavior, implementations are required to track, alongside each PSK established by a previous connection, <del> whether the connection negotiated this extension with the "ech_accept" response type.  If so, this is referred to as an "ECH PSK".  Otherwise, it is a "non-ECH PSK".  This may be implemented by adding a new field to client and server session states. </del>  6.1.

Reviewer comments:
Section 6: implementations to track, alongside each PSK established by a previous connection, whether the connection negotiated this extension with the "echaccept" response type. If so, this is referred to as an "ECH PSK". Otherwise, it is a "non-ECH PSK". This may be implemented by adding a new field to client and server session states. "echaccept" is not defined anywhere.
I suspect "the connection negotiated this extension with the 'ech_accept' response type" is an incorrect way of saying that ECH was accepted.

Revised text:
Per client-behavior and server-behavior, implementations are required to track, alongside each PSK established by a previous connection,  <ins> whether ECH was accepted for this connection.  If so, this is referred to as an "ECH PSK".  Otherwise, it is a "non-ECH PSK".  This may be implemented by adding a new field to client and server session states. </ins> 6.1.
Original text:
the query domain name.  That is, if a client queries example.com, the ESNI TXT Resource Record might be: <del> Servers SHOULD configure DNS such that, upon querying a domain name with ESNI support, at most one each of A, AAAA, TXT ESNI, and ALTSVC I-D.schwartz-httpbis-dns-alt-svc Resource Record is returned.  Alt- Svc records may be used to inform the client of the plaintext </del>  (fronting) SNI.  If present, clients SHOULD use its value in the SNI extension of the subsequent ClientHello.

Reviewer comments:
See
This looks good to me. Clarifying the requirements rather than specifying how to act makes more sense here. FWIW, I thought the intent of the original text was to encourage not having ESNIKeys shared between multiple hosts whenever possible. This change loses that property. But anyways, that is mentioned in the appendix. Hence my :+1: to the PR.
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.
Regarding: Why is that a SHOULD? Does it apply to both fronting and shared mode? For reference, Cloudflare almost always sends at least two A and AAAA answers for every question. (I get that it's not a MUST, though I'd also like to understand what the reasoning is in the first place)
NAME ??
The idea was to simplify key management. If multiple A or AAAA records are returned, the server needs to ensure each server behind those addresses have all the necessary keys.
NAME I see, I'd rather the spec said that instead of trying to impose limits (I made with suggested fix).
NAME works for me :)

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, 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. Alt-Svc records may be used to inform the client of the plaintext </ins> (fronting) SNI.  If present, clients SHOULD use its value in the SNI extension of the subsequent ClientHello.
Original text:
does a standard ClientHello, with the exception of the following rules: <del> It MUST NOT offer to negotiate TLS 1.2 or below. </del>  It MUST NOT offer to resume any non-ECH PSK or any session for TLS 1.2 and below. 7.1. Upon receiving an "encrypted_client_hello" extension, the client- <del> facing server MUST check that it is able to negotiate TLS 1.3 or greater.  If not, it MUST abort the connection with a "handshake_failure" alert. </del>  The ClientECH value is said to match a known ECHConfig if there exists an ECHConfig that can be used to successfully decrypt

Reviewer comments:
The version check on the outer ClientHello doesn't make much sense since, when ECH is accepted, the version is based on the inner ClientHello anyway. See URL I've left a TODO since it seems weird to have a client requirement like that be so load-bearing without a corresponding server check. Not sure where we should go there.
I like this change, but it might be worth resolving the TODO.
Removed the TODO in favor of some clarifying text on the client side.

Revised text:
does a standard ClientHello, with the exception of the following rules:  <ins> It MUST NOT offer to negotiate TLS 1.2 or below.  Note this is necessary to ensure the backend server does not negotiate a TLS version that is incompatible with ECH. </ins> It MUST NOT offer to resume any non-ECH PSK or any session for TLS 1.2 and below. 7.1. Upon receiving an "encrypted_client_hello" extension, the client-  <ins> facing server determines if it will accept ECH, prior to negotiating any other TLS parameters.  Note that successfully decrypting the extension will result in a new ClientHello to process, so even the client's TLS version preferences may have changed. </ins> The ClientECH value is said to match a known ECHConfig if there exists an ECHConfig that can be used to successfully decrypt
Original text:
10.10.3. <del> This attack uses resumption PSKs as an oracle for dictionary attacks against a given ClientHello's true SNI.  To begin, the attacker first interacts with a server to obtain a resumption ticket for a given test domain, such as "test.com".  Later, upon receipt of a legitimate ClientHello without a PSK binder, it computes a PSK binder using its own ticket and forwards the resulting ClientHello.  Assume the server then validates the PSK binder on the outer ClientHello and chooses connection parameters based on the inner ClientHello.  A server which then validates information in ClientHelloOuter ticket against information in ClientHelloInner, such as the SNI, introduces an oracle that can be used to test the encrypted SNI value of specific ClientHello messages. ECH mitigates against this attack by (1) prohibiting the "pre_shared_key" extension in ClientHelloOuter and (2) requiring servers ignore this extension when processing ClientHelloOuter. Thus, if the server accepts the inner ClientHello, it only validates binders in the inner ClientHello.  This means that PSKs are used within the HPKE encryption envelope. </del>  11.

Reviewer comments:
I think this is a more general attack and deserves a more general description of the mitigation. See URL and . NAME

Revised text:
10.10.3.  <ins> This attack aims to leak information about secret parts of the encrypted ClientHello by adding attacker-controlled parameters and observing the server's response.  In particular, the compression mechanism described in outer-extensions references parts of a potentially attacker-controlled ClientHelloOuter to construct ClientHelloInner, or a buggy server may incorrectly apply parameters from ClientHelloOuter to the handshake. To begin, the attacker first interacts with a server to obtain a resumption ticket for a given test domain, such as "example.com". Later, upon receipt of a ClientHelloOuter, it modifies it such that the server will process the resumption ticket with ClientHelloInner. If the server only accepts resumption PSKs that match the server name, it will fail the PSK binder check with an alert when ClientHelloInner is for "example.com" but silently ignore the PSK and continue when ClientHelloInner is for any other name.  This introduces an oracle for testing encrypted SNI values. This attack may be generalized to any parameter which the server varies by server name, such as ALPN preferences. ECH mitigates this attack by only using ClientHelloOuter to compute ClientHelloInner and authenticating all of ClientHelloInner with the HPKE AEAD.  If any extensions are compressed as in outer-extensions, the OuterExtensions.inner_digest field authenticates the decompressed result.  If none are compressed, the entire ClientHello is encrypted and authenticated directly.  An earlier iteration of this specification only encrypted and authenticated the "server_name" extension, which left the overall ClientHello vulnerable to an analogue of this attack. </ins> 11.
Original text:
The client might duplicate non-sensitive extensions in both messages. However, implementations need to take care to ensure that sensitive <del> extensions are not offered in the ClientHelloOuter.  [[OPEN ISSUE: We should provide guidance on what extensions are sensitive and suggest suitable substitutes.]] </del>  To encrypt EncodedClientHelloInner, the client first needs to generate the HPKE encryption context.  It computes the encapsulated values associated with the true server name in the ClientHelloOuter. It MAY send such values in the ClientHelloInner.  Values which are independent of the true server name, or other information the client wishes to protect, MAY be included in <del> ClientHelloOuter and compressed as described in outer-extensions. </del>  10.6.

Reviewer comments:
I noticed there's an OPEN ISSUE to add ClientHelloOuter guidance, but there's already a section for it. Link to it and flesh it out a little more. Sadly, in doing so, I've traded one OPEN ISSUE for another OPEN ISSUE, but hopefully that'll be closed shortly.

Revised text:
The client might duplicate non-sensitive extensions in both messages. However, implementations need to take care to ensure that sensitive  <ins> extensions are not offered in the ClientHelloOuter.  See outer- clienthello for additional guidance. </ins> To encrypt EncodedClientHelloInner, the client first needs to generate the HPKE encryption context.  It computes the encapsulated values associated with the true server name in the ClientHelloOuter. It MAY send such values in the ClientHelloInner. <ins> A client may also use different preferences in different contexts. For example, it may send a different ALPN lists to different servers or in different application contexts.  A client that treats this context as sensitive SHOULD NOT send context-specific values in ClientHelloOuter. </ins> Values which are independent of the true server name, or other information the client wishes to protect, MAY be included in  <ins> ClientHelloOuter.  If they match the corresponding ClientHelloInner, they MAY be compressed as described in outer-extensions.  However, note the payload length reveals information about which extensions are compressed, so inner extensions which only sometimes match the corresponding outer extension SHOULD NOT be compressed. [[OPEN ISSUE: In addition to the fuzzy leak from the length, there is an active attack to probe compressed extensions.  See issue #323.  We should either document this attack, or bind ClientHelloOuter.]] Clients MAY include additional extensions in ClientHelloOuter to avoid signaling unusual behavior to passive observers, provided the choice of value and value itself are not sensitive.  See do-not- stick-out. </ins> 10.6.
Original text:
the one hand, the requirements on info seem weaker, but maybe actually this needs to be secret?  Analysis needed.]]  6.4. If the client attempts to connect to a server and does not have an

Reviewer comments:
Issue .
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:
the one hand, the requirements on info seem weaker, but maybe actually this needs to be secret?  Analysis needed.]] <ins> [[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.  Stateless HRR on the client- facing server works fine, however.  See issue #333.]] </ins> 6.4. If the client attempts to connect to a server and does not have an
Original text:
case, it does not make any changes to the TLS messages, but just blindly forwards them. <del> If the server sends a NewSessionTicket message, the corresponding PSK MUST be ignored by all other servers in the deployment when not negotiating ECH, including servers which do not implement this specification. </del> 7.1.1. It is an error for the client to offer ECH before the

Reviewer comments:
This clause is a remnant of when we tried to partition the resumption space across ECH negotiation, which was simplified in .

Revised text:
case, it does not make any changes to the TLS messages, but just blindly forwards them.  7.1.1. It is an error for the client to offer ECH before the
Original text:
To encrypt EncodedClientHelloInner, the client first needs to generate the HPKE encryption context.  It computes the encapsulated <del> key, context, HRR key (see hrr), and payload as: </del>  Note that the HPKE functions Deserialize and SetupBaseS are those which match "ECHConfig.kem_id" and the AEAD/KDF used with "context" ClientHelloInner from EncodedClientHelloInner, as described in outer- extensions. <del> Upon determining the true SNI, the client-facing server then either serves the connection directly (if in Shared Mode), in which case it executes the steps in the following section, or forwards the TLS connection to the backend server (if in Split Mode).  In the latter case, it does not make any changes to the TLS messages, but just blindly forwards them. </del>  7.1.1.

Reviewer comments:
This fixes a few issues with the "Upon determining the true SNI" paragraph: It talkes about determining the "true SNI", but we should probably talk about the ClientHelloInner more generally. It refers to behavior in "the following section" which no longer matches. This is a remnant of before Shared Mode was described as a special case of Split Mode, so remove it. It says the server forwards messages unmodified, which isn't true. It needs to forward the decrypted ClientHello. It also needs to process HRR. I've cited the HRR section for the modified decryption procedure, which doesn't really exist right now, but is handling that.
Yup, this aligns with my understanding!

Revised text:
To encrypt EncodedClientHelloInner, the client first needs to generate the HPKE encryption context.  It computes the encapsulated  <ins> key, context, HRR key (see client-hrr), and payload as: </ins> Note that the HPKE functions Deserialize and SetupBaseS are those which match "ECHConfig.kem_id" and the AEAD/KDF used with "context" ClientHelloInner from EncodedClientHelloInner, as described in outer- extensions.  <ins> Upon determining the ClientHelloInner, the client-facing server then forwards the ClientHelloInner to the appropriate backend server, which proceeds as in backend-server-behavior.  If the backend server responds with a HelloRetryRequest, the client-facing server forwards it, decrypts the client's second ClientHelloOuter using the modified procedure in server-hrr, and forwards the resulting second ClientHelloInner.  The client-facing server forwards all other TLS messages between the client and backend server unmodified. </ins> 7.1.1.
Original text:
IANA is requested to create the following two entries in the existing registry for ExtensionType (defined in RFC8446): <del> encrypted_client_hello(0xff08), with "TLS 1.3" column values being </del>  set to "CH, EE", and "Recommended" column being set to "Yes". <del> outer_extensions(0xff04), with the "TLS 1.3" column values being </del>  set to "", and "Recommended" column being set to "Yes". 11.2.

Reviewer comments:
cc NAME NAME
Yeah, that's what I figured. I doubt we'll need more than 8, but we can pick a different range if that works?
Yeah, this works for me. I don't see any reason why we should keep the code points dense.
Note to self. Let's pick a set and range that works.

Revised text:
IANA is requested to create the following two entries in the existing registry for ExtensionType (defined in RFC8446):  <ins> encrypted_client_hello(0xfe08), with "TLS 1.3" column values being </ins> set to "CH, EE", and "Recommended" column being set to "Yes".  <ins> outer_extensions(0xfd00), with the "TLS 1.3" column values being </ins> set to "", and "Recommended" column being set to "Yes". 11.2.
Original text:
"ECHConfig.cipher_suites" list. The configuration identifier, equal to "Expand(Extract("", <del> config), "tls ech config id", Nh)", where "config" is the "ECHConfig" structure and "Extract", "Expand", and "Nh" are as specified by the cipher suite KDF.  (Passing the literal """" as the salt is interpreted by "Extract" as no salt being provided.) The length of this value SHOULD NOT be less than 16 bytes unless it is optional for an application; see optional-configs. </del>  The HPKE encapsulated key, used by servers to decrypt the corresponding "payload" field. but MAY be held constant for successive connections to the same server in the same session. <del> Set the "config_id" field to a randomly-generated string of "Nh" bytes, where "Nh" is the output length of the "Extract" function of the KDF associated with the chosen cipher suite.  (The KDF API is specified in I-D.irtf-cfrg-hpke.) </del>  Set the "enc" field to a randomly-generated valid encapsulated public key output by the HPKE KEM. is determined by one of the two following methods: Compare ClientECH.config_id against identifiers of known <del> ECHConfigs and select the one that matches, if any, as a candidate. </del>  Collect all known ECHConfigs as candidates, with trial decryption below determining the final selection.

Reviewer comments:
This doesn't bind anything, so no sense wasting bytes. (Note: this is orthogonal to , but it makes more sense with that PR.)
I guess this makes the text in a little odd because "Compare ClientECH.config_id against identifiers of known ECHConfigs and select the one that matches, if any, as a candidate.", is a little less clear. If we want to do this, it maybe should say "and select any that match", even though there'll really just be one match.
(Rebased and made the text talk about multiple potential matches.)
This matches the acceptance signal length, and we've already accepted that probability of collision.
I agree this is fine.

Revised text:
"ECHConfig.cipher_suites" list. The configuration identifier, equal to "Expand(Extract("",  <ins> 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 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. but MAY be held constant for successive connections to the same server in the same session.  <ins> Set the "config_id" field to a randomly-generated 8-byte string. </ins> Set the "enc" field to a randomly-generated valid encapsulated public key output by the HPKE KEM. is determined by one of the two following methods: Compare ClientECH.config_id against identifiers of known  <ins> ECHConfigs and select the ones that match, if any, as candidates. </ins> Collect all known ECHConfigs as candidates, with trial decryption below determining the final selection.