An (EC)DH key share (attached to the label)  The list of keys which can be used by the client to encrypt the SNI. names it supports, and will not work if the mapping between the fronting server and the hidden server uses wildcards.]]  A list of extensions that the client can take into consideration when generating a Client Hello message.  The format is defined in I-D.ietf-tls-tls13; Section 4.2.  The purpose of the field is to probability may be increased via traffic analysis or other mechanisms. <del> The Resource Record TTL determines the lifetime of the published ESNI keys.  Clients MUST NOT use ESNI keys beyond their published lifetime.  Note that the length of this structure MUST NOT exceed 2^16 - 1, as the RDLENGTH is only 16 bits RFC1035. </del>  5.

I expect that the resistance against ESNIKeys will come from web-site owners worrying about ESNIKeys being additional source of service disruption. One might argue that the probability of disruption becomes 2x because now two resource records (A and esni) are involved instead of just one. One might argue that the probability is higher than that because there could be more issues with carrying large TXT records than carrying A records. One might argue that the probability is below 2x because the two records are transmitted through the same path. Anyways, these arguments are about the quantitative difference about the probability. I prefer having a qualitative guarantee, that assures that there would be no increase in the probability of service disruption assuming that the owner of the record does not misconfigure the service. Having a checksum and validity period fields that can be validated by the client provides such guarantee. Checksum provides protection against accidental corruption. Validity period provides protection against misconfiguration or bugs in the DNS servers. It acts as a protection against an outdated ESNIKeys sent by an out-of-sync authoritative server or a buggy resolver causing disruption. The downside of having checksum and validity is that the additional payload (currently 20 bytes, I think we can minimize this to 10 bytes by removing (it's not that needed) and changing the type of from to ), and that the client would be required to do the validation. But I assume that having qualitative guarantee that the probability of service disruption remains the same is worth the effort.
NAME The PR is now aligned with master. I think we should merge this and rebase upon this regardless of what we end up with on the PR.
Does the advertisement really need a checksum? It complicates things for questionable benefit. Use DNS-over-something-that-isn't-as-lame-as-UDP and this problem goes away. You probably want DNS to be protected by TLS anyway.
As discussed on URL and URL, I prefer to have the checksum for end-to-end integrity checking and to not have increased risk of service disruption for better adoption of ESNI; otherwise the hosts that using ESNI would stick out. It's not only when transmitting data over the wire that the ESNIKeys might get corrupt. That could happen before the record gets installed on the authoritative server, or while the data is being cached in a resolver. Or we might have other conveyer in the future.
Yeah, I don't find those arguments particularly persuasive. The biggest risk is misconfiguration, which - given the nature of this - won't be persistent. Sites that do this will test their code and once it is fixed, it should work. As for network-level corruption, that's rare, and DNS over TLS (or DoH) aren't really vulnerable to that.
I agree that corruption is rare; the question is if it is as rare as TCP transferring corrupt data. I would like to also point out that corruption cannot be immediately fixed; you need to wait for the cached records to expire. And even if you use DNS over TLS or DOH between the client and the resolver, you could still be using unauthenticated DNS over UDP between the resolver and the authoritative server.
NAME NAME shall we consider this issue resolved?
Considering resolved.
Doing so doesn't allow a server to gracefully generate new ESNI keys and retire old ones. We should mandate that clients always choose the first ESNI key for which they have group support. If we adopt , where servers lookup keys with a (label, group) -> key mapping, should we also require that each label have the same groups?
NAME what do you think?
I am not sure what you mean by "doing so", but the model I expect is something like following assuming that we adopt . the fronting server generates a new ESNIKeys and pushes it to DNS server every 1 hour TTL will be set to 5 minutes 1 the fronting server will accept the following keys to be used in the EncryptedSNI extension: the key for the current hour the key for the previous hour, for the first 10 minutes of every hour A ESNIKeys record can contain multiple keys belonging to different groups, but not belonging to the same group. So there is no ambiguity by specifying the key using (label, group). The server can add a new Named Group or retire one when it generates a new ESNIKeys. [1] updated to reflect the fact that we can only use a constant TTL in the standard way of DNS server synchronization (i.e. DNS zone transfer) (02:47UTC May 15 2018)
That seems perfectly fine. But it does mean that there exists only one key per group at any given point in time. Is that a restriction we are comfortable with?
I don't think so. A DNS server can send different ESNIKeys at the same hour using short TTL (e.g. 60 seconds) in response to different queries (a.k.a. round-robin DNS).
Sorry, I mean within a single record.
I am comfortable with not having that. You can only have a handful of keys in ESNIKeys anyways. I'd assume that either one would be enough or we would need a way to distribute more than just a handful, for which we can use round-robin.
SGTM. We should add this rationale as text to the DNS record section.
I think that TTL suffices here.
NAME FWIW, is now in master. We can certainly discuss about the necessity, but you might want to see the debate on .
NAME I think your point in the PR is quite valid. However it's probably worth noting that having explicit notbefore / notafter that the client has to validate, is likely to make it very difficult, if not impossible, to deploy very-short-lived keys (say, in the order of a few hours), due to the fact that a significant portion of clients have shitty clocks (URL), which affects forward secrecy. Maybe it should be noted in the spec as an appendix or something.
Hmmm, thinking about this some more. In the "split brain" case NAME described in , the not_after is used to avoid having clients send an ESNI encrypted with an old key (as fetched from an out-of-date DNS server), to a TLS server that might have already switched to a new key (thus causing the connection to break). So i'm guessing what happens is that the client would fallback to plaintext SNI if the key it fetched from DNS is expired. Though it seems to me this fallback could be dangerous for the client since they would end up exposing the SNI, but I can't come up with a practical way to exploit this (could be dependent on the specific deployment I guess). Whether this is something worth fixing is a different question, but an alternative to fix the split brain problem would be for the TLS server to somehow make sure the new key has been received by the DNS server, before discarding the old one. Did I understand the problem you described in correctly NAME ?
NAME Thank you for your comments. I think you are correct in stating that the ESNIKeys cannot have a very short lifetime. However, I would like to argue that the fact does not prevent server operators from rotating the keys frequently. Server operators can rotate the keys at the speed they like, as well as retiring them from the servers in any way. I agree with what happens. In my view, ESNI is opportunistic encryption in sense that the unencrypted hostname is used when the ESNI resource record fails to reach the client (including the case of a middlebox dropping it intentionally). I would argue that using SNI in case of receiving an expired ESNI record is one of the failure modes in distributing the correct resource record. And therefore, I do not think that it is a security concern. That is correct. However, I fear that having such a requirement hinders us from using existing DNS deployments, because DNS authoritative servers are designed to favor availability over consistency, whereas in our case without , we need consistency over availability.
I didn't realize that was possible (that is, I kind of assumed servers would have to keep the key around until the notafter expired, but it's not actually written anywhere). In that case fine, though it might be worth clarifying. The other concern I had was risking of turning this into another OCSP (where network failure and attack basically look the same so clients end up ignoring both), but I guess that with DoH, DPRIVE and DNSSEC an attack here is much harder to pull off. So all in all I don't have a strong opinion on notbefore / not_after anymore.
I think this is fine, NAME please re-open if not.
The biggest concern from web-site operators will be the possibility of out-of-sync or broken ESNIKeys leading to service disruption. Considering that, I propose having two attributes being added: validity - we can borrow and from PKIX checksum - something like 32-bits would be enough Seeing wired ESNIKeys will become a big issue especially if the clients accept ESNIKeys sent from low-quality resolvers / proxies (e.g. those in home routers). PS. One alternative approach would be to use HTTP (.well-known URI) as a transport of the semi-static key between the TLS server and the resolver. Then, we can use the vocabulary of HTTP to implement client-side verification.

An (EC)DH key share (attached to the label) <ins> First four (4) octets of the SHA-256 message digest RFC6234 of the ESNIKeys structure starting from the first octet of "keys" to the end of the stucture. </ins> The list of keys which can be used by the client to encrypt the SNI. names it supports, and will not work if the mapping between the fronting server and the hidden server uses wildcards.]] <ins> The moment when the keys become valid for use.  The value is represented as seconds from 00:00:00 UTC on Jan 1 1970, not including leap seconds. The moment when the keys become invalid.  Uses the same unit as not_before. </ins> A list of extensions that the client can take into consideration when generating a Client Hello message.  The format is defined in I-D.ietf-tls-tls13; Section 4.2.  The purpose of the field is to probability may be increased via traffic analysis or other mechanisms.  <ins> The "checksum" field provides protection against transmission errors, including those caused by intermediaries such as a DNS proxy running on a home router.  "not_before" and "not_after" fields represent the validity period of the published ESNI keys.  Clients MUST NOT use ESNI keys that was covered by an invalid checksum or beyond the published period.  Servers SHOULD set the Resource Record TTL small enough so that the record gets discarded by the cache before the ESNI keys reach the end of their validity period. Note that the length of this structure MUST NOT exceed 2^16 - 1, as the RDLENGTH is only 16 bits RFC1035. </ins> 5.


"echo_nonce" to the ClientHelloInner (but not to the outer ClientHello).  This nonce ensures that the server's encrypted Certificate can only be read by the entity which sent this <del> ClientHello.  [[TODO: Describe HRR cut-and-paste 1 in Security Considerations.]] This extension is defined as follows: </del>  A 16-byte nonce exported from the HPKE encryption context.  See send-echo for details about its computation. Clients then encrypt the second ClientHelloInner using this new HPKE context.  In doing so, the encrypted value is also authenticated by <del> echo_hrr_key. </del>  Client-facing servers perform the corresponding process when decrypting second ClientHelloInner messages.  In particular, upon a RECOMMENDED padding mechanism for clients aimed at reducing potential information leakage.  11. 11.1.

Addresses . This is for motivating some of our design decisions (and documenting what we know along the way). I meant to do this a while ago, but never got around to it. In particular, it describes why we have the following three bindings: Transcript to ECHO secret (to prevent client reaction attacks). CH1 to CH2 (to prevent HRR oracles). ClientHello to resumption PSK binder (to prevent resumption oracles). I'm sure the language and presentation can be improved, so suggestions to that effect are very much welcome! cc NAME NAME NAME NAME NAME NAME NAME
Thanks for the feedback! I incorporated your suggestions and clarified a few more things. I'm merging as-is since they seemed mostly editorial.
Addressed in .

"echo_nonce" to the ClientHelloInner (but not to the outer ClientHello).  This nonce ensures that the server's encrypted Certificate can only be read by the entity which sent this  <ins> ClientHello.  This extension is defined as follows: </ins> A 16-byte nonce exported from the HPKE encryption context.  See send-echo for details about its computation. Clients then encrypt the second ClientHelloInner using this new HPKE context.  In doing so, the encrypted value is also authenticated by  <ins> echo_hrr_key.  The rationale for this is described in flow-hrr- hijack. </ins> Client-facing servers perform the corresponding process when decrypting second ClientHelloInner messages.  In particular, upon a RECOMMENDED padding mechanism for clients aimed at reducing potential information leakage. <ins> 10.8. This section describes the rationale for ECHO properties and mechanics as defenses against active attacks.  In all the attacks below, the attacker is on-path between the target client and server. The goal of the attacker is to learn private information about the inner ClientHello, such as the true SNI value. 10.8.1. This attack uses the client's reaction to an incorrect certificate as an oracle.  The attacker intercepts a legitimate ClientHello and replies with a ServerHello, Certificate, CertificateVerify, and Finished messages, wherein the Certificate message contains a "test" certificate for the domain name it wishes to query.  If the client decrypted the Certificate and failed verification (or leaked information about its verification process by a timing side channel), the attacker learns that its test certificate name was incorrect.  As an example, suppose the client's SNI value in its inner ClientHello is "example.com," and the attacker replied with a Certificate for "test.com".  If the client produces a verification failure alert because of the mismatch faster than it would due to the Certificate signature validation, information about the name leaks.  Note that the attacker can also withhold the CertificateVerify message.  In that scenario, a client which first verifies the Certificate would then respond similarly and leak the same information. The "echo_nonce" extension in the inner ClientHello prevents this attack.  In particular, since the attacker does not have access to this value, it cannot produce the right transcript and handshake keys needed for encrypting the Certificate message.  Thus, the client will fail to decrypt the Certificate and abort the connection. 10.8.2. This attack aims to exploit server HRR state management to recover information about a legitimate ClientHello using its own attacker- controlled ClientHello.  To begin, the attacker intercepts and forwards a legitimate ClientHello with an "encrypted_client_hello" (echo) extension to the server, which triggers a legitimate HelloRetryRequest in return.  Rather than forward the retry to the client, the attacker, attempts to generate its own ClientHello in response based on the contents of the first ClientHello and HelloRetryRequest exchange with the result that the server encrypts the Certificate to the attacker.  If the server used the SNI from the first ClientHello and the key share from the second (attacker- controlled) ClientHello, the Certificate produced would leak the client's chosen SNI to the attacker. This attack is mitigated by binding the first and second ClientHello messages together.  In particular, since the attacker does not possess the echo_hrr_key, it cannot generate a valid encryption of the second inner ClientHello.  The server will attempt decryption using echo_hrr_key, detect failure, and fail the connection. If the second ClientHello were not bound to the first, it might be possible for the server to act as an oracle if it required parameters from the first ClientHello to match that of the second ClientHello. For example, imagine the client's original SNI value in the inner ClientHello is "example.com", and the attacker's hijacked SNI value in its inner ClientHello is "test.com".  A server which checks these for equality and changes behavior based on the result can be used as an oracle to learn the client's SNI. 10.8.3. 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 the outer ClientHello ticket against information in the inner ClientHello, such as the SNI, introduces an oracle that can be used to test the encrypted SNI value of specific ClientHello messages. ECHO mitigates against this attack by requiring servers not mix-and- match information from the inner and outer ClientHello.  For example, if the server accepts the inner ClientHello, it does not validate binders in the outer ClientHello.  This means that ECHO PSKs are used within the HPKE encryption envelope. </ins> 11. 11.1.


Set the "enc" field to a randomly-generated valid encapsulated public key output by the HPKE KEM. <del> Set the "encrypted_ch" field to a randomly-generated string of [TODO] bytes. </del>  If the server sends an "encrypted_client_hello" extension, the client MUST check the extension syntactically and abort the connection with

This assumes the GREASE threat model is a misbehaving and lazy middlebox. That is, we aim to prevent ossification. It does nothing more to prevent an active attacker from learning whether or not a particular connection used GREASE. (That problem is probably better suited to something like MASQUE.) cc NAME NAME NAME
Merging for now. We can come back later and address followup extensions if needed!
The draft has provisions for GREASE ESNI extensions, per "Do not stick out". However, this is not very effective if an attacker can distinguish a GREASE ESNI extension from a real one. tried to address this, but it doesn't work if the (public) ESNI record is known. There's also a simpler attack: replay the ClientHello with one byte of ciphertext corrupted. ESNI currently distinguishes key mismatch from decrypt failure, with the latter resulting in an alert. We can fix that by saying decrypt failure should behave like key mismatch. This still leaves recorddigest. The natural fix is to drop recorddigest in favor of trial decryption. The cost is the server must perform a DH operation per known key. The number of keys the server needs depends on its DNS TTLs and key rotation. (I think it's 1 + ceil(dnsttl / timebetweenrotation), plus some leeway[].) Instead, we can keep a key name, but make the space dense. Suppose we replace it with a B-bit "key phase". The server picks a random starting key phase. Then, each time it rotates the keys, it increments the phase, with wraparound. The server then gets 2B keys for free. If it needs more, it still needs trial decryption but gains a 2B multiplicative factor in the cost. On the flip side, an individual GREASE extension with a random key phase has a 1/2B chance of colliding with a particular ESNI key. I'm not sure we actually need that many live keys, so B = 2 is probably plenty. Or even B = 1 if we say you should just rotate slower than your DNS TTL. [] Note the robustness mechanism means that server doesn't need to cover all clients with stale keys. The retry is expensive, so the server still needs to cover almost all_ of them, but it can cut off the long tail.
Mostly a note to myself for the actual PR: the draft should also discuss padding of the EncryptedExtensions message.
I think we really need to go back to first principles here and ask what it is we are trying to accomplish. First, it's public information whether a given server supports ECHO, so if an attacker wants to know that, it can mostly just ask [there is some complexity around getting candidate names, but note that you can always get the public name from the SNI value and any real attacker will see a lot of non-ECHO traffic and so will know a bunch of SNI values]. What's not public information is whether a given connection uses ECHO. The attacker might not know this either because: It knows that the server is ECHO-supporting. It has a limited view of just the connection and isn't willing to do do the work of trying to determine if the server is ECHO-supporting. In case (1) I would also expect that the attacker is not going to go to the trouble of doing probe connections, as determining if the server is ECHO supporting is easier. This leaves us with the case where the attacker knows that the server is ECHO supporting and that the client knows about ECHO (otherwise it couldn't generate GREASE) but doesn't know if the client is actually doing ECHO. Do we expect there to be a lot of clients like this? Why not just do ECHO?
As per today's call, we're going to revisit the GREASE threat model and then reconvene if further work is needed.
I'm moving to closes this issue as resolved by . If we need to do something against connection blocking adversaries, perhaps we should look into MASQUE for that.
Closing for now. NAME please re-open if you still think we should address this!

Set the "enc" field to a randomly-generated valid encapsulated public key output by the HPKE KEM.  <ins> Set the "encrypted_ch" field to a randomly-generated string of L bytes, where L is the size of the ClientHelloInner message the client would use given an ECHOConfig structure, padded according to padding. </ins> If the server sends an "encrypted_client_hello" extension, the client MUST check the extension syntactically and abort the connection with


<del> Encrypted ClientHello for TLS 1.3 </del>  draft-ietf-tls-esni-latest Abstract <del> This document defines a mechanism for encrypting a TLS 1.3 ClientHello. </del>  1. Encrypted Client Hello (ECH), which allows clients to encrypt the entirety of their ClientHello to a supporting server.  This protects the SNI and other potentially sensitive fields, such as the ALPN <del> list. </del>  2.

Seeing no pushback on the list, this moves the draft from Experimental to ST.
The design can apply to TLS 1.4 and beyond (presumably).
Done in .

<ins> TLS Encrypted Client Hello </ins> draft-ietf-tls-esni-latest Abstract  <ins> This document describes a mechanism in Transport Layer Security (TLS) for encrypting a ClientHello message under a server public key. </ins> 1. Encrypted Client Hello (ECH), which allows clients to encrypt the entirety of their ClientHello to a supporting server.  This protects the SNI and other potentially sensitive fields, such as the ALPN  <ins> list.  This extension is only supported with (D)TLS 1.3 RFC8446 and newer versions of the protocol. </ins> 2.


The ECHConfig structure contains the following fields: The version of the structure.  For this specification, that value <del> SHALL be 0xff03.  Clients MUST ignore any ECHConfig structure with </del>  a version they do not understand. An opaque byte string whose contents depend on the version of the 11.1. <del> IANA is requested to create an entry, encrypted_client_hello(0xffce), in the existing registry for ExtensionType (defined in RFC8446), with "TLS 1.3" column values being set to "CH, EE", and "Recommended" column being set to "Yes". </del>  11.2.

Once we have some implementations that interop, we'll go ahead with early codepoint allocation. The new version matches -07, which will be the next draft version we submit. cc NAME NAME
We need to pick some values for the "encryptedclienthello(TBD)" and for the HTTPSSVC RR type before we can do interop. Allocating some values from the private ranges for those before draft-07 is pushed out would be good so we can do some interop testing. (Or for whatever is the next draft for which we want to do interop.)

The ECHConfig structure contains the following fields: The version of the structure.  For this specification, that value  <ins> SHALL be 0xff07.  Clients MUST ignore any ECHConfig structure with </ins> a version they do not understand. An opaque byte string whose contents depend on the version of the 11.1.  <ins> IANA is requested to create the following two entries in the existing registry for ExtensionType (defined in RFC8446): encrypted_client_hello(57), with "TLS 1.3" column values being set to "CH, EE", and "Recommended" column being set to "Yes". outer_extension(58), with the "TLS 1.3" column values being set to "CH", and "Recommended" column being set to "Yes". </ins> 11.2.


SNI Encryption keys can be published in the DNS using the ESNIKeys structure, defined below. <del> An opaque label to use for a given key. </del>  <del> An (EC)DH key share (attached to the label) </del>  The list of keys which can be used by the client to encrypt the <del> SNI. </del>  The length to pad the ServerNameList value to prior to encryption. This value SHOULD be set to the largest ServerNameList the [[OPEN ISSUE: An alternative to padding is to instead send a hash of the server name.  This would be fixed-length, but have the disadvantage that the server has to retain a table of all the server <del> names it supports.]] </del>  The semantics of this structure are simple: any of the listed keys may be used to encrypt the SNI for the associated domain name.  The probability may be increased via traffic analysis or other mechanisms. <del> The Resource Record TTL determines the lifetime of the published ESNI keys.  Clients MUST NOT use ESNI keys beyond their published lifetime.  Note that the length of this structure MUST NOT exceed 2^16 - 1, as the RDLENGTH is only 16 bits RFC1035. </del>  5. The label associated with the SNI encryption key.  The cipher suite used to encrypt the SNI. The original ServerNameList from the "server_name" extension, In order to send an encrypted SNI, the client MUST first select one of the server ESNIKeyShareEntry values and generate an (EC)DHE share <del> in the matching group.  If multiple keys (labels) for the same IP address are available, clients SHOULD choose one at random.  This share is then used for the client's "key_share" extension and will be used to derive both the SNI encryption key the (EC)DHE shared secret which is used in the TLS key schedule.  This has two important implications: </del>  The client MUST only provide one KeyShareEntry "illegal_parameter" alert.  [[OPEN ISSUE: We looked at ignoring the extension but concluded this was better.]]  If more than one KeyShareEntry has been provided, or if that share's group does not match that for the SNI encryption key, it MUST abort the connection with an "illegal_parameter" alert. the corresponding private key to decrypt a key.  Thus, when one ESNI key is provided, sharing is optimally bound by the number of hosts that share an IP address.  Server operators may further limit sharing <del> by including multiple keys, with distinct labels, in an ESNIKeys structure. </del>  7.2.3.

In ESNI, the server offers a list of ECDH keys and the client selects one of them. However, there seems to be nothing that prevents a downgrade attack. This PR adds the protection, by sending the list of the Named Groups being offered by ESNIKeys in the EncryptedSNI extension. The bonus is that the size of ESNIKeys becomes smaller when more than one keys are offered (because we now only send one label per the entire key set).
Consider the case where the authoritative server sends an esni record containing X448 and X25519 keys. Malicious middlebox rewrites the esni record to only contain the X25519 key. The client will accept the X25519 key and use that for the TLS handshake. I think that is not prevented now, and that we need to prevent it.
NAME thanks for identifying this. I agree it's a real issue and wish I'd caught it before :( Thinking about defenses, do you think it might make more sense to ditch the label and instead just carry a hash of the record? Then we could guarantee that it wasn't changed more easily? OTOH, that would preclude self-encryption of the ESNI Key....
NAME Servers might want more than one label per record to allow for migration. Why don't we have clients send the hash of the ESNKey structure for the label it chooses? The server could also include that in full in place of the truncated hash.
NAME I meant that we don't need to send the NamedGroupsList for mitigation -- we could send something else, e.g., the ESNIKey hash.
NAME The other issue is that you then need to consider negotiating the hash algorithm to be used. I did not want to deal with that and therefore went for the Named Group list. NAME Thank you for the clarification. I think the paragraph right above is my answer.
NAME The checksum you propose is already computed with SHA256 -- why do you think this is something we ought to negotiate?
NAME The checksum is only 32-bits and it's for detecting errors, not preventing attacks. Named Group selection is a cryptographic property of the TLS handshake and it needs to be protected by the digest algorithm of the negotiated cipher-suite. It is either a full-length SHA-256 or SHA-384 at the moment, but could change in the future.
NAME The intent of this is to convey to the server that the client saw the right set of options before it chose its key. We don't need to consider any properties of NamedGroup selection with TLS to do so. And picking a suitable cryptographic hash function here is good enough. I really don't think we need agility here. Also, I wasn't suggesting the client echo the 32bit hash found in the record. It should send the full SHA256 hash.
Oh, also, what if we decide to add an extension that could influence how clients choose keys? Wouldn't we want downgrade protection for that, too? (A hash would work for that, whereas echoing the NamedGroup list would not.)
My understanding is as follows: In TLS 1.3, client offers the Named Groups and server selects one. It is protected by the digest algorithm of the negotiated cipher-suite. In TLS 1.3 + ESNI, server offers the Named Groups and client selects one. Therefore, I assume that which Named Groups we offered needs to be protected by the digest algorithm of the negotiated cipher-suite, or else we are changing the security properties of TLS 1.3. I agree. So to me the choice seems to be between a) choose a specific solution (i.e. send Named Group list) or b) add digest algorithm negotiation and send the hash value. I went for a, but I won't argue hardly against b.
Well, we could just use the hash associated with the cipher suite, no?
NAME That's possible, but then the server needs to offer a list of hash algorithms that it understands in ESNIKeys, or the client needs to send hash digests for all the cipher-suites it offers.
NAME I don't think that's necessary. We can assume SHA256 or some other MTI hash function for this to work. In the interest of future proofing, let's use a hash. I care much less about agility for the algorithm in question.
Assuming that is true, I am happy to go for using hash. The primary reason is that we can remove and use the hash value as the key to identify the ESNIKeys record that the client used. OTOH, I am still not convinced why we do not need to negotiate the hash algorithm, especially when we are talking about the possibility of transferring additional extensions in the future that will be under the same protection. But I am not a crypto person and I could well be wrong, especially when it is midnight here. Anyways thank you very much for your quick response and for quickly merging the other PR. This PR is now in sync with master. Please feel free to use this as a basis of creating a PR that implements hash-based protection, if the current text looks worth enough.
Interesting point about using the hash as the label, though it might help to keep the label in the event that we come up with another way to publish ESNI keys. Regarding negotiation, I think SHA256 is sufficient for now and the future. Negotiating yet another algorithm seems like unnecessary complexity here. I can take a stab at writing that text, though I think what you have here is nearly there.
Sorry, I'm confused: the DNS record contains a list oc cipher suites, each of which is associated with a hash. The client chooses one to use to encrypt the SNI. Why can't that be the one you use to hash the ESNIKeys record
NAME That'd work fine, I just think it's unnecessary here.
So there's the argument that always using SHA-256 should be fine, and there's the argument that that the cipher-suites list can sent in ESNIKeys to solve the issue. I disagree to both. I have already explained why I think using SHA256 always is not fine. Let me explain why I think sending cipher-suites list in ESNIKeys is not fine. NAME Traditionally, we have allowed different cipher-suites to be selected depending on the value of SNI. This becomes an even more important property in the ESNI proposal, because we split the server into fronting server and hidden server. Cipher-suite selection is a negotiation between the client and the hidden server, whereas the ESNIKeys is a key-exchange between the client and the fronting server. Therefore, we cannot / should not use ESNIKeys as a conveyer of cipher-suite negotiation. Considering that we have disagreement on the how we can uses hashes, maybe it is a good idea to merge this as-is to fix the security hole, and get back to do the improvements later on if necessary. I thought that we wanted to submit this soon.
NAME The ciphersuite that's sent by the client in the EncryptedSNI extension only applies to that extension, not to the rest of the handshake or data, right?
NAME Ah! That's correct. We now have two cipher-suites. I am fine with using the cipher-suite then.
Cool -- I'll propose some text. :-)
Applied NAME patch that changes the protection from using a list of Named Groups to using a digest specified by the cipher-suite.
recorddigest is sent together with encryptedsni in EncryptedSNI which is supposed to be the sensitive information you want to protect from a downgrade attack. So assuming there is a downgrade attack (I'm guessing you get bad key information from the DNS record) the client falls for it, sends the ESNI in a way that allows it to be compromised, and also sends the hash for the server to check whether an attack is going on. Fronting Server Behavior text says "If the EncryptedSNI.record_digest value does not match the cryptographic hash of the associated ENSIKeys structure, it MUST abort the connection" By the time the server realizes the hash is incorrect and drops the connection the ESNI has already been sent over the wire by the client. (And the adversary has intercepted it) So what's the benefit of dropping the connection at that point? As a side-note, can you explain the specific downgrade attacks you have in mind? My understanding is that the threat here is learning the value of SNI. If you are able to tamper with the DNS record then by definition you know (or can learn) the client's query for the domain name. Why would you need to attack the key material and try to decrypt the ESNI? And on the other hand, if you don't have r/w access to the DNS records, how can you carry out a downgrade attack?
That's a good question. The issue here is the Named Group that is being selected for ESNI protection key will also be used for negotiating the handshake. So the TLS handshake gets compromised (and that is what we are calling downgrade attack here). Please refer to the first paragraph for URL
Can we treat the ESNI key derivation and TLS key derivation as separate processes? Seems like the reason we need to protect against downgrade attacks is because we are reusing the (potentially poisoned) ESNIKeys KeyShareEntry structure in the TLS key schedule. If we were to limit its use to just ESNI key derivation would we still need to protect against downgrade attacks?
My understanding is that that is possible, and then we do not need to send the digest value of the ESNIKeys structure assuming that we do not add extensions that need downgrade protection. However, that would require the client to send and generate one more ECDH key, as well as increasing the probability of HRR due to group mismatch. To put it another way, the approach used in this PR is on par with the amount of the data transmitted (the size of the digest and the ECDH key are roughly equal), has the possibility of cutting a round-trip, requires less CPU cost.
Doing so doesn't allow a server to gracefully generate new ESNI keys and retire old ones. We should mandate that clients always choose the first ESNI key for which they have group support. If we adopt , where servers lookup keys with a (label, group) -> key mapping, should we also require that each label have the same groups?
NAME what do you think?
I am not sure what you mean by "doing so", but the model I expect is something like following assuming that we adopt . the fronting server generates a new ESNIKeys and pushes it to DNS server every 1 hour TTL will be set to 5 minutes 1 the fronting server will accept the following keys to be used in the EncryptedSNI extension: the key for the current hour the key for the previous hour, for the first 10 minutes of every hour A ESNIKeys record can contain multiple keys belonging to different groups, but not belonging to the same group. So there is no ambiguity by specifying the key using (label, group). The server can add a new Named Group or retire one when it generates a new ESNIKeys. [1] updated to reflect the fact that we can only use a constant TTL in the standard way of DNS server synchronization (i.e. DNS zone transfer) (02:47UTC May 15 2018)
That seems perfectly fine. But it does mean that there exists only one key per group at any given point in time. Is that a restriction we are comfortable with?
I don't think so. A DNS server can send different ESNIKeys at the same hour using short TTL (e.g. 60 seconds) in response to different queries (a.k.a. round-robin DNS).
Sorry, I mean within a single record.
I am comfortable with not having that. You can only have a handful of keys in ESNIKeys anyways. I'd assume that either one would be enough or we would need a way to distribute more than just a handful, for which we can use round-robin.
SGTM. We should add this rationale as text to the DNS record section.
I think that TTL suffices here.
NAME FWIW, is now in master. We can certainly discuss about the necessity, but you might want to see the debate on .
NAME I think your point in the PR is quite valid. However it's probably worth noting that having explicit notbefore / notafter that the client has to validate, is likely to make it very difficult, if not impossible, to deploy very-short-lived keys (say, in the order of a few hours), due to the fact that a significant portion of clients have shitty clocks (URL), which affects forward secrecy. Maybe it should be noted in the spec as an appendix or something.
Hmmm, thinking about this some more. In the "split brain" case NAME described in , the not_after is used to avoid having clients send an ESNI encrypted with an old key (as fetched from an out-of-date DNS server), to a TLS server that might have already switched to a new key (thus causing the connection to break). So i'm guessing what happens is that the client would fallback to plaintext SNI if the key it fetched from DNS is expired. Though it seems to me this fallback could be dangerous for the client since they would end up exposing the SNI, but I can't come up with a practical way to exploit this (could be dependent on the specific deployment I guess). Whether this is something worth fixing is a different question, but an alternative to fix the split brain problem would be for the TLS server to somehow make sure the new key has been received by the DNS server, before discarding the old one. Did I understand the problem you described in correctly NAME ?
NAME Thank you for your comments. I think you are correct in stating that the ESNIKeys cannot have a very short lifetime. However, I would like to argue that the fact does not prevent server operators from rotating the keys frequently. Server operators can rotate the keys at the speed they like, as well as retiring them from the servers in any way. I agree with what happens. In my view, ESNI is opportunistic encryption in sense that the unencrypted hostname is used when the ESNI resource record fails to reach the client (including the case of a middlebox dropping it intentionally). I would argue that using SNI in case of receiving an expired ESNI record is one of the failure modes in distributing the correct resource record. And therefore, I do not think that it is a security concern. That is correct. However, I fear that having such a requirement hinders us from using existing DNS deployments, because DNS authoritative servers are designed to favor availability over consistency, whereas in our case without , we need consistency over availability.
I didn't realize that was possible (that is, I kind of assumed servers would have to keep the key around until the notafter expired, but it's not actually written anywhere). In that case fine, though it might be worth clarifying. The other concern I had was risking of turning this into another OCSP (where network failure and attack basically look the same so clients end up ignoring both), but I guess that with DoH, DPRIVE and DNSSEC an attack here is much harder to pull off. So all in all I don't have a strong opinion on notbefore / not_after anymore.
I think this is fine, NAME please re-open if not.
The text in section 4, Publishing the SNI Encryption Key, specifies that if a client queries URL, the ESNI TXT name is URL That's fine, but suppose that the client queries "URL". The client needs to learn that "URL" is fronted by "URL" -- per PR , the client will do that by obtaining an ALT-SVC record. Should the client get the ESNI key from "URL", or from "URL" ?
NAME I think this is you.
covers this. Mostly. It might pay to observe that the client does not look up and DNS records for the fronting service, it is just the case that all servers in the anonymity set point to the same IP address. I made similar comments on .
I agree with NAME here. I didn't see the client querying URL
That means all servers that share the same IP need to publish the same key. Are we confident that there is no synch issue during key rollover?
Servers can publish multiple ESNI keys with different labels in a record. That way they can retire one key and start using another as needed. Though it might be good (necessary?) to require clients to use one of those keys.
While that is possible, I am not sure if it would be the approach that would generally be used. The issue is identical to the encryption key of the session tickets. In case of session tickets, we encrypt the ticket using one key, and send the identifier of the key along with the encrypted ticket. When we rollover the keys, we first deploy a new key to the server as well as keeping the old one alive for decryption only. And we retire the old key after certain amount of period that we become sure that it is no longer used. I'd assume that we can and will use the same approach for ESNIKeys.
PS. therefore, it is possible to have just one label for the set of keys rather than having labels for each of the keys (assuming that we can remove the "choose one at random" rule). In that approach, a server will identify the key being used by looking at the tuple. The benefit will be that you will save the space of N-1 labels assuming that you are sending N key shares. Identification of the key on the server-side might seem complicated, but I actually think that it simplifies things, because a server operator is anyways expected to deploy the entire set of keys found in ESNIKeys at the same moment.
To me it seems that this is necessary to prevent downgrade attack. Hence .
How do we prevent labels from becoming identifiers?
Labels as identifiers seem problematic in both approaches -- current and .
Would you mind elaborating why label becomes an identifier? We state in the document that the probability of identifying the hidden server is 1/N where N is the number of the hosts belonging to the same set of hidden servers. A label represents that entire set. So I do not think it is an issue.
Now that is merged, which removes the label altogether, and we've added text describing the anonymity set for hidden servers, I think this issue is covered. NAME do you agree?
The label issue is definitely covered.
This LGTM. NAME PTAL when you have time.

SNI Encryption keys can be published in the DNS using the ESNIKeys structure, defined below.  <ins> An opaque label used to identify the list of keys. </ins>  <ins> First four (4) octets of the SHA-256 message digest RFC6234 of the ESNIKeys structure starting from the first octet of "keys" to the end of the stucture. </ins> The list of keys which can be used by the client to encrypt the  <ins> SNI.  Every key being listed MUST belong to a different group. </ins> The length to pad the ServerNameList value to prior to encryption. This value SHOULD be set to the largest ServerNameList the [[OPEN ISSUE: An alternative to padding is to instead send a hash of the server name.  This would be fixed-length, but have the disadvantage that the server has to retain a table of all the server  <ins> names it supports, and will not work if the mapping between the fronting server and the hidden server uses wildcards.]] The moment when the keys become valid for use.  The value is represented as seconds from 00:00:00 UTC on Jan 1 1970, not including leap seconds. The moment when the keys become invalid.  Uses the same unit as not_before. A list of extensions that the client can take into consideration when generating a Client Hello message.  The format is defined in I-D.ietf-tls-tls13; Section 4.2.  The purpose of the field is to provide room for additional features in the future; this document does not define any extension. </ins> The semantics of this structure are simple: any of the listed keys may be used to encrypt the SNI for the associated domain name.  The probability may be increased via traffic analysis or other mechanisms.  <ins> The "checksum" field provides protection against transmission errors, including those caused by intermediaries such as a DNS proxy running on a home router.  "not_before" and "not_after" fields represent the validity period of the published ESNI keys.  Clients MUST NOT use ESNI keys that was covered by an invalid checksum or beyond the published period.  Servers SHOULD set the Resource Record TTL small enough so that the record gets discarded by the cache before the ESNI keys reach the end of their validity period. Note that the length of this structure MUST NOT exceed 2^16 - 1, as the RDLENGTH is only 16 bits RFC1035. </ins> 5. The label associated with the SNI encryption key. <ins> A cryptographic hash of the ESNIKeys structure from which the label and ESNI key was obtained, i.e., from "checksum" to the end of the structure.  This hash is computed using the hash function associated with "suite". </ins> The cipher suite used to encrypt the SNI. The original ServerNameList from the "server_name" extension, In order to send an encrypted SNI, the client MUST first select one of the server ESNIKeyShareEntry values and generate an (EC)DHE share  <ins> in the matching group.  This share is then used for the client's "key_share" extension and will be used to derive both the SNI encryption key the (EC)DHE shared secret which is used in the TLS key schedule.  This has two important implications: </ins> The client MUST only provide one KeyShareEntry "illegal_parameter" alert.  [[OPEN ISSUE: We looked at ignoring the extension but concluded this was better.]] <ins> If the EncryptedSNI.record_digest value does not match the cryptographic hash of the associated ENSIKeys structure, it MUST abort the connection with an "illegal_parameter" alert.  This is necessary to prevent downgrade attacks. </ins> If more than one KeyShareEntry has been provided, or if that share's group does not match that for the SNI encryption key, it MUST abort the connection with an "illegal_parameter" alert. the corresponding private key to decrypt a key.  Thus, when one ESNI key is provided, sharing is optimally bound by the number of hosts that share an IP address.  Server operators may further limit sharing  <ins> by sending different Resource Records containing ESNIKeys with different keys covered by different labels using a short TTL. </ins> 7.2.3.


disabled by the server.  It SHOULD retry the handshake with a new transport connection and ECH disabled. <del> [[TODO: Key replacement is significantly less scary than saying that ECH-naive servers bounce ECH off.  Is it worth defining a strict mode toggle in the ECH keys, for a deployment to indicate it is ready for that? ]] </del> Clients SHOULD implement a limit on retries caused by "ech_retry_request" or servers which do not acknowledge the "encrypted_client_hello" extension.  If the client does not retry in 10.6. <del> Note that the backend server has no way of knowing what the SNI was, but that does not lead to additional privacy exposure because the backend server also only has one identity.  This does, however, change the situation slightly in that the backend server might previously have checked SNI and now cannot (and an attacker can route a connection with an encrypted SNI to any backend server and the TLS connection will still complete).  However, the client is still responsible for verifying the server's identity in its certificate. [[TODO: Some more analysis needed in this case, as it is a little odd, and probably some precise rules about handling ECH and no SNI uniformly?]] 10.7. </del> Variations in the length of the ClientHelloInner ciphertext could leak information about the corresponding plaintext. padding describes a RECOMMENDED padding mechanism for clients aimed at reducing potential information leakage. <del> 10.8. </del>  This section describes the rationale for ECH properties and mechanics as defenses against active attacks.  In all the attacks below, the of the attacker is to learn private information about the inner ClientHello, such as the true SNI value. <del> 10.8.1. </del>  This attack uses the client's reaction to an incorrect certificate as an oracle.  The attacker intercepts a legitimate ClientHello and needed for encrypting the Certificate message.  Thus, the client will fail to decrypt the Certificate and abort the connection. <del> 10.8.2. </del>  This attack aims to exploit server HRR state management to recover information about a legitimate ClientHello using its own attacker- for equality and changes behavior based on the result can be used as an oracle to learn the client's SNI. <del> 10.8.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

The misrouting content no longer applies since the backend server sees the full CH, with the true SNI.

disabled by the server.  It SHOULD retry the handshake with a new transport connection and ECH disabled.  Clients SHOULD implement a limit on retries caused by "ech_retry_request" or servers which do not acknowledge the "encrypted_client_hello" extension.  If the client does not retry in 10.6.  Variations in the length of the ClientHelloInner ciphertext could leak information about the corresponding plaintext. padding describes a RECOMMENDED padding mechanism for clients aimed at reducing potential information leakage.  <ins> 10.7. </ins> This section describes the rationale for ECH properties and mechanics as defenses against active attacks.  In all the attacks below, the of the attacker is to learn private information about the inner ClientHello, such as the true SNI value.  <ins> 10.7.1. </ins> This attack uses the client's reaction to an incorrect certificate as an oracle.  The attacker intercepts a legitimate ClientHello and needed for encrypting the Certificate message.  Thus, the client will fail to decrypt the Certificate and abort the connection.  <ins> 10.7.2. </ins> This attack aims to exploit server HRR state management to recover information about a legitimate ClientHello using its own attacker- for equality and changes behavior based on the result can be used as an oracle to learn the client's SNI.  <ins> 10.7.3. </ins> This attack uses resumption PSKs as an oracle for dictionary attacks against a given ClientHello's true SNI.  To begin, the attacker first


The client then generates a ClientHelloInner value.  In addition to the normal values, ClientHelloInner MUST also contain: <del> an "ech_nonce" extension </del>  TLS padding RFC7685 (see padding) IANA is requested to create the following two entries in the existing registry for ExtensionType (defined in RFC8446): <del> encrypted_client_hello(57), with "TLS 1.3" column values being set to "CH, EE", and "Recommended" column being set to "Yes". </del>  <del> outer_extension(58), with the "TLS 1.3" column values being set to </del>  "CH", and "Recommended" column being set to "Yes".  11.2. IANA is requested to create an entry, ech_required(121) in the

We'll request early codepoints after we get some implementation experience.
Yeah, as I was specifying the extension codepoint I noticed the collision between this variable, so I figured changing it here was best.
I have no objection to echononce -> echononce_value but did you mean to do it in this PR?

The client then generates a ClientHelloInner value.  In addition to the normal values, ClientHelloInner MUST also contain:  <ins> an "ech_nonce" extension, containing "ech_nonce_value" derived above </ins> TLS padding RFC7685 (see padding) IANA is requested to create the following two entries in the existing registry for ExtensionType (defined in RFC8446):  <ins> encrypted_client_hello(0xff02), with "TLS 1.3" column values being set to "CH, EE", and "Recommended" column being set to "Yes". </ins>  <ins> ech_nonce(0xff03), with the "TLS 1.3" column values being set to </ins> "CH", and "Recommended" column being set to "Yes". <ins> outer_extension(0xff04), with the "TLS 1.3" column values being set to "CH", and "Recommended" column being set to "Yes". </ins> 11.2. IANA is requested to create an entry, ech_required(121) in the


Optional record digests may be useful in scenarios where clients and client-facing servers do not want to reveal information about the client-facing server in the "encrypted_client_hello" extension.  In <del> such settings, servers must perform trial decrypt upon receipt of an empty record digest, which may exacerbate DoS attacks.  Specifically, an adversary may send malicious ClientHello messages, i.e., those which will not decrypt with any known ECH key, in order to force wasteful decryption.  Servers that support this feature should, for example, implement some form of rate limiting mechanism to limit the damage caused by such attacks. </del>  10.4.

As discussed on the list, add a small bit of text in the section 10.3 to outline the behavior of clients seeking to protect server's privacy.
Thanks!

Optional record digests may be useful in scenarios where clients and client-facing servers do not want to reveal information about the client-facing server in the "encrypted_client_hello" extension.  In  <ins> such settings, clients send either an empty record_digest or a randomly generated record_digest in the ClientEncryptedCH.  (The precise implementation choice for this mechanism is out of scope for this document.)  Servers in these settings must perform trial decryption since they cannot identify the client's chosen ECH key using the record_digest value.  As a result, support for optional record digests may exacerbate DoS attacks.  Specifically, an adversary may send malicious ClientHello messages, i.e., those which will not decrypt with any known ECH key, in order to force wasteful decryption.  Servers that support this feature should, for example, implement some form of rate limiting mechanism to limit the damage caused by such attacks. </ins> 10.4.


The serialized and encrypted ClientHelloInner structure, AEAD- encrypted using HPKE with the selected KEM, KDF, and AEAD <del> algorithm and key generated as described below. </del>  If the server accepts ECH, it does not send this extension.  If it rejects ECH, then it sends the following structure in

This ensures that each paragraph line breaks at 80 characters. No other change is intended (editorial or otherwise).
Do you want to borrow the linter that we use in QUIC? Just copy to your Makefile along with .
Brilliant!

The serialized and encrypted ClientHelloInner structure, AEAD- encrypted using HPKE with the selected KEM, KDF, and AEAD  <ins> algorithm and key generated as described below.  {:br} </ins> If the server accepts ECH, it does not send this extension.  If it rejects ECH, then it sends the following structure in


Although TLS 1.3 RFC8446 encrypts most of the handshake, including the server certificate, there are several ways in which an on-path attacker can learn private information about the connection.  The <del> cleartext Server Name Indication (SNI) extension in ClientHello </del>  messages, which leaks the target domain for a given connection, is perhaps the most sensitive information unencrypted in TLS 1.3. The target domain may also be visible through other channels, such as <del> cleartext client DNS queries, visible server IP addresses (assuming </del>  the server does not use domain-based virtual hosting), or other indirect mechanisms such as traffic analysis.  DoH I-D.ietf-doh-dns- over-https and DPRIVE RFC7858 RFC8094 provide mechanisms for clients the SNI remains the primary explicit signal used to determine the server's identity. <del> The TLS WG has extensively studied the problem of protecting the SNI, </del>  but has been unable to develop a completely generic solution.  I- D.ietf-tls-sni-encryption provides a description of the problem space and some of the proposed techniques.  One of the more difficult the result often has undesirable performance consequences, incomplete coverage, or both. <del> The design in this document takes a different approach: it assumes that private origins will co-locate with or hide behind a provider (CDN, application server, etc.) which can protect SNIs for all of the domains it hosts.  As a result, SNI protection does not indicate that the client is attempting to reach a private origin, but only that it is going to a particular service provider, which the observer could already tell from the visible IP address. The design in this document introduces a new extension, called Encrypted Client Hello (ECH), which allows clients to encrypt the entirety of their ClientHello to a supporting server.  This protects the SNI and other potentially sensitive fields, such as the ALPN list.  This extension is only supported with (D)TLS 1.3 RFC8446 and newer versions of the protocol. </del>  2. 3. <del> This document is designed to operate in one of two primary topologies shown below, which we call "Shared Mode" and "Split Mode" </del>  3.1. In Shared Mode, the provider is the origin server for all the domains <del> whose DNS records point to it and clients form a TLS connection directly to that provider, which has access to the plaintext of the connection. </del>  <del> In Split Mode, the provider is _not_ the origin server for private domains.  Rather the DNS records for private domains point to the </del>  provider, and the provider's server relays the connection back to the <del> backend server, which is the true origin server.  The provider does not have access to the plaintext of the connection. </del>  3.2. <del> ECH works by encrypting the entire ClientHello, including the SNI and any additional extensions such as ALPN.  This requires that each provider publish a public key and metadata which is used for ClientHello encryption for all the domains for which it serves directly or indirectly (via Split Mode).  This document defines the format of the SNI encryption public key and metadata, referred to as an ECH configuration, and delegates DNS publication details to HTTPS- RR, though other delivery mechanisms are possible.  In particular, if some of the clients of a private server are applications rather than Web browsers, those applications might have the public key and metadata preconfigured. When a client wants to form a TLS connection to any of the domains served by an ECH-supporting provider, it constructs a ClientHello in the regular fashion containing the true SNI value (ClientHelloInner) and then encrypts it using the public key for the provider.  It then constructs a new ClientHello (ClientHelloOuter) with an innocuous SNI (and potentially innocuous versions of other extensions such as ALPN RFC7301) and containing the encrypted ClientHelloInner as an extension.  It sends ClientHelloOuter to the server. Upon receiving ClientHelloOuter, the server can then decrypt ClientHelloInner and either terminate the connection (in Shared Mode) or forward it to the backend server (in Split Mode). Note that both ClientHelloInner and ClientHelloOuter are both valid, complete ClientHello messages.  ClientHelloOuter carries an encrypted representation of ClientHelloInner in a "encrypted_client_hello" extension, defined in encrypted-client-hello. </del>  4. The serialized and encrypted ClientHelloInner structure, AEAD- encrypted using HPKE with the selected KEM, KDF, and AEAD <del> algorithm and key generated as described below.  {:br} </del>  If the server accepts ECH, it does not send this extension.  If it rejects ECH, then it sends the following structure in When the server cannot decrypt or does not process the "encrypted_client_hello" extension, it continues with the handshake <del> using the cleartext "server_name" extension instead (see server- </del>  behavior).  Clients that offer ECH then authenticate the connection with the public name, as follows: correspond to ECH PSKs.  ECH PSKs offered by the client are associated with the ECH name.  The server was unable to decrypt then ECH name, so it should not resume them when using the <del> cleartext SNI name.  This restriction allows a client to reject </del>  resumptions in auth-public-name. Note that an unrecognized ClientEncryptedCH.record_digest value may Unlike most TLS extensions, placing the SNI value in an ECH extension is not interoperable with existing servers, which expect the value in <del> the existing cleartext extension.  Thus server operators SHOULD </del>  ensure servers understand a given set of ECH keys before advertising them.  Additionally, servers SHOULD retain support for any previously-advertised keys for the duration of their validity Assuming ECH records retrieved from DNS are authenticated, e.g., via DNSSEC or fetched from a trusted Recursive Resolver, spoofing a <del> server operating in Split Mode is not possible.  See cleartext-dns for more details regarding cleartext DNS. </del>  Authenticating the ECHConfigs structure naturally authenticates the included public name.  This also authenticates any retry signals from

Use the term "plaintext" consistently. Be consistent about the number of spaces following a period. (We'll go with one.) Editorial changes to Sec.1-3.

Although TLS 1.3 RFC8446 encrypts most of the handshake, including the server certificate, there are several ways in which an on-path attacker can learn private information about the connection.  The  <ins> plaintext Server Name Indication (SNI) extension in ClientHello </ins> messages, which leaks the target domain for a given connection, is perhaps the most sensitive information unencrypted in TLS 1.3. The target domain may also be visible through other channels, such as  <ins> plaintext client DNS queries, visible server IP addresses (assuming </ins> the server does not use domain-based virtual hosting), or other indirect mechanisms such as traffic analysis.  DoH I-D.ietf-doh-dns- over-https and DPRIVE RFC7858 RFC8094 provide mechanisms for clients the SNI remains the primary explicit signal used to determine the server's identity.  <ins> The TLS Working Group has studied the problem of protecting the SNI, </ins> but has been unable to develop a completely generic solution.  I- D.ietf-tls-sni-encryption provides a description of the problem space and some of the proposed techniques.  One of the more difficult the result often has undesirable performance consequences, incomplete coverage, or both.  <ins> The protocol specified by this document takes a different approach. It assumes that private origins will co-locate with or hide behind a provider (reverse proxy, application server, etc.) that protects SNIs for all of the domains it hosts.  As a result, SNI protection does not indicate that the client is attempting to reach a private origin, but only that it is going to a particular service provider, which the observer could already tell from the visible IP address. This document specifies a new TLS extension, called Encrypted Client Hello (ECH), that allows clients to encrypt their ClientHello to a supporting server.  This protects the SNI and other potentially sensitive fields, such as the ALPN list RFC7301.  This extension is only supported with (D)TLS 1.3 RFC8446 and newer versions of the protocol. </ins> 2. 3.  <ins> This protocol is designed to operate in one of two topologies illustrated below, which we call "Shared Mode" and "Split Mode". </ins> 3.1. In Shared Mode, the provider is the origin server for all the domains  <ins> whose DNS records point to it.  In this mode, the TLS connection is terminated by the provider. </ins>  <ins> In Split Mode, the provider is not the origin server for private domains.  Rather, the DNS records for private domains point to the </ins> provider, and the provider's server relays the connection back to the  <ins> origin server, who terminates the TLS connection with the client. Importantly, service provider does not have access to the plaintext of the connection. In the remainder of this document, we will refer to the ECH-service provider as the "client-facing server" and to the TLS terminator as the "backend server".  These are the same entity in Shared Mode, but in Split Mode, the client-facing and backend servers are physically separated. </ins> 3.2.  <ins> ECH allows the client to encrypt sensitive ClientHello extensions, e.g., SNI, ALPN, etc., under the public key of the client-facing server.  This requires the client-facing server to publish the public key and metadata it uses for ECH for all the domains for which it serves directly or indirectly (via Split Mode).  This document defines the format of the ECH encryption public key and metadata, referred to as an ECH configuration, and delegates DNS publication details to HTTPS-RR, though other delivery mechanisms are possible. In particular, if some of the clients of a private server are applications rather than Web browsers, those applications might have the public key and metadata preconfigured. When a client wants to establish a TLS session with the backend server, it constructs its ClientHello as usual (we will refer to this as the ClientHelloInner message) and then encrypts this message using the ECH public key.  It then constructs a new ClientHello (ClientHelloOuter) with innocuous values for sensitive extensions, e.g., SNI, ALPN, etc., and with the encrypted ClientHelloInner in an "encrypted_client_hello" extension, which this document defines (encrypted-client-hello).  Finally, it sends ClientHelloOuter to the server. Upon receiving ClientHelloOuter, the client-facing server processes the encrypted ClientHelloInner and either terminates the connection (in Shared Mode) or forwards it to the backend server (in Split Mode). </ins> 4. The serialized and encrypted ClientHelloInner structure, AEAD- encrypted using HPKE with the selected KEM, KDF, and AEAD  <ins> algorithm and key generated as described below. {:br} </ins> If the server accepts ECH, it does not send this extension.  If it rejects ECH, then it sends the following structure in When the server cannot decrypt or does not process the "encrypted_client_hello" extension, it continues with the handshake  <ins> using the plaintext "server_name" extension instead (see server- </ins> behavior).  Clients that offer ECH then authenticate the connection with the public name, as follows: correspond to ECH PSKs.  ECH PSKs offered by the client are associated with the ECH name.  The server was unable to decrypt then ECH name, so it should not resume them when using the  <ins> plaintext SNI name.  This restriction allows a client to reject </ins> resumptions in auth-public-name. Note that an unrecognized ClientEncryptedCH.record_digest value may Unlike most TLS extensions, placing the SNI value in an ECH extension is not interoperable with existing servers, which expect the value in  <ins> the existing plaintext extension.  Thus server operators SHOULD </ins> ensure servers understand a given set of ECH keys before advertising them.  Additionally, servers SHOULD retain support for any previously-advertised keys for the duration of their validity Assuming ECH records retrieved from DNS are authenticated, e.g., via DNSSEC or fetched from a trusted Recursive Resolver, spoofing a  <ins> server operating in Split Mode is not possible.  See plaintext-dns for more details regarding plaintext DNS. </ins> Authenticating the ECHConfigs structure naturally authenticates the included public name.  This also authenticates any retry signals from


[[OPEN ISSUE: An alternative to padding is to instead send a hash of the server name.  This would be fixed-length, but have the disadvantage that the server has to retain a table of all the server <del> names it supports.]] </del>  A list of extensions that the client can take into consideration when generating a Client Hello message.  The format is defined in

The approach does not work if the mapping between the fronting server and the hidden server contains a wildcard.
LGTM!
Current text says: the "encryptedservername" extension. However, I am not sure if this is the desired behavior. I would rather suggest encrypting even when the hostname exceeds the padded_length. Having some protection is better than none. The issue is that in certain deployments it is impossible to guess what the maximum length of the hostname will be. Consider . It uses a wildcard certificate. Anybody can add a new hostname of any length by creating a new Github account (it might be true that Github some limitation on the length, but that does not necessarily mean that Github shares the maximum length information with the fronting CDN, assuming that it uses a CDN).
I have filed that fixes another issue (it's just a clarification of an open issue) related to wildcard delegation.
Technically, it is possible to guess the maximum length of the hostname, as that maximum textual representation length is defined as 253 bytes per RFC 1034.
Yes. So the question is if a CDN that has a wildcard mapping needs to set to 253 (since current text is a MUST NOT for sending a server name longer than the paddedlength), or if we should introduce a different rule.
How about something like: padded_length : The length to pad the ServerNameList value to prior to encryption. This value SHOULD be set to the largest ServerNameList the fronting server expects to support rounded up the nearest multiple of 16. If the fronting server supports wildcard names, it SHOULD set this value to 256.
NAME Thank you for the suggestion. I was feeling uneasy (and am still feeling a bit) about requiring the client to send an ESNI extension as large as ~300 bytes, assuming that CDN's would have at least one wildcard mappings. But it's about the first packet sent from the client, and we typically have enough room even when 0-RTT data is involved. So I think that we can live with that.
To be

[[OPEN ISSUE: An alternative to padding is to instead send a hash of the server name.  This would be fixed-length, but have the disadvantage that the server has to retain a table of all the server  <ins> names it supports, and will not work if the mapping between the fronting server and the hidden server uses wildcards.]] </ins> A list of extensions that the client can take into consideration when generating a Client Hello message.  The format is defined in


server's length hint L (ECHCOnfig.maximum_name_length) when computing the padding as follows: <del> If L > D, add L - D bytes of padding.  This rounds to the server's advertised hint, i.e., ECHConfig.maximum_name_length. </del>  <del> Otherwise, add 32 - (D % 32) bytes of padding.  This rounds D up to the nearest multiple of 32 bytes. </del>  In addition to padding ClientHelloInner, clients and servers will also need to pad all other handshake messages that have sensitive-

Consider the following values for L and D, where L is the server's hint and D is the length of the domain name. This change pads D according to the following rules. D1: D L is at least 32 elements. Thanks, NAME
cc NAME NAME NAME NAME NAME
TBH I am not sure that this is worth it, but I believe it is correct.
Section 7.2 says This has some problems: If L == D, or D > L and D % 32 == 0, this results in unnecessary padding. If L == D == 31, there is no length protection. The output is 32, and that is the only length that produces that output. The easiest solution is to replace "32 - (D % 32)" with "(L - D) & 0x1f". This pads up to the nearest L + 32*n. If it's considered important to pad to a multiple of 32, I would suggest ensuring that the initial bucket is at least 32, e.g.

server's length hint L (ECHCOnfig.maximum_name_length) when computing the padding as follows:  <ins> If L >= D, add L - D bytes of padding.  This rounds to the server's advertised hint, i.e., ECHConfig.maximum_name_length. </ins>  <ins> Otherwise, let P = 31 - ((D - 1) % 32), and add P bytes of padding, plus an additional 32 bytes if D + P < L + 32.  This rounds D up to the nearest multiple of 32 bytes that permits at least 32 bytes of length ambiguity. </ins> In addition to padding ClientHelloInner, clients and servers will also need to pad all other handshake messages that have sensitive-


OuterExtensions.hash, the server aborts the connection with an "illegal_parameter" alert. <del> Clients SHOULD only use this mechanism for extensions which are large.  All other extensions SHOULD appear in both ClientHelloInner and ClientHelloOuter even if they have identical values. </del> 7. 7.1.

This addresses .
In Section 6.1: if I were implementing this extension, I think the simplest thing would be to compress all copied extensions by default: it's not computationally intensive; it minimizes bandwidth overhead; and it's simpler for future "big" extensions, since no code changes would be needed here. I suggest removing this guidance.
addresses this issue.

OuterExtensions.hash, the server aborts the connection with an "illegal_parameter" alert.  7. 7.1.


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_keys" field from the server's "encrypted_client_hello" extension. </del>  If one of the values contains a version supported by the client, it can regard the ECH keys as securely replaced by the server.  It introducing pinning concerns or a tracking vector, should a malicious server present client-specific retry keys to identify clients. <del> If none of the values provided in "retry_keys" contains a supported version, the client can regard ECH as securely disabled by the server.  As below, it SHOULD then retry the handshake with a new </del>  transport connection and ECH disabled. If the field contains any other value, the client MUST abort the connection, with the following added behavior: It MUST include the "encrypted_client_hello" extension with the <del> "retry_keys" field set to one or more ECHConfig structures with </del>  up-to-date keys.  Servers MAY supply multiple ECHConfig values of different versions.  This allows a server to support multiple versions at once.

Addresses
The spec uses both and when referring to the contents of . These should be made consistent (preferably using the former).
Agreed -- let's unify around ! Would you be able to propose text?
( is a remnant of the old name. I guess we missed a few.)
Thanks!

Otherwise, when the handshake completes successfully with the public name authenticated, 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. </ins> If one of the values contains a version supported by the client, it can regard the ECH keys as securely replaced by the server.  It introducing pinning concerns or a tracking vector, should a malicious server present client-specific retry keys to identify clients.  <ins> If none of the values provided in "retry_configs" contains a supported version, the client can regard ECH as securely disabled by the server.  As below, it SHOULD then retry the handshake with a new </ins> transport connection and ECH disabled. If the field contains any other value, the client MUST abort the connection, with the following added behavior: It MUST include the "encrypted_client_hello" extension with the  <ins> "retry_configs" field set to one or more ECHConfig structures with </ins> up-to-date keys.  Servers MAY supply multiple ECHConfig values of different versions.  This allows a server to support multiple versions at once.


carrying the "HpkePublicKey" and KEM identifier corresponding to the server, clients compute an HPKE encryption context as follows: <del> Note that the HPKE algorithm identifiers are those which match the client's chosen preference from "ECHConfig.cipher_suites". </del>  The client then generates a ClientHelloInner value.  In addition to the normal values, ClientHelloInner MUST also contain:

draft-irtf-cfrg-hpke-05 switched from Unmarshal to Deserialize. Switch ECH to match. See .
Cause I'm quite new to ECH, I'm little bit confused when reading this draft. So I want to ask some questions... Paragraph 7.1. Sending an encrypted ClientHello says... Q1. Is 'Unmarshal()' above equal to general meaning of unmarshalling -get public key from ECHConfig structure- ? Q2. I think 'hrr' in the fourth line is the abbreviation of 'HelloRetryRequest', is it correct? If so, could you add some explanation about that?
Q1: It should be defined in the HPKE draft, though it looks like the HPKE draft has since renamed it and it should probably say Deserialize now. URL Q2: hrr is HelloRetryRequest, yes. The use of the key is described later on in the HelloRetryRequest section. URL
The first line appears to be pseudocode for "interpret as a public key for the KEM algorithm identified by . IMO we should get rid of this pseudocode and say what is. NAME beat me to it!
Oh, it was Deserialize()... thanks!
Closing as resolved.

carrying the "HpkePublicKey" and KEM identifier corresponding to the server, clients compute an HPKE encryption context as follows:  <ins> Note that the HPKE functions Deserialize and SetupBaseS are those which match "ECHConfig.kem_id" and the client's chosen preference from "ECHConfig.cipher_suites". </ins> The client then generates a ClientHelloInner value.  In addition to the normal values, ClientHelloInner MUST also contain:


match a value provided in the corresponding "ECHConfig.cipher_suites" list. <del> Equal to "Expand(Extract("", config), "ech_config_digest", 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.) </del>  The HPKE encapsulated key, used by servers to decrypt the corresponding "encrypted_ch" field. cipher_suite contains the client's chosen HpkeCipherSuite; <del> config_digest contains the digest of the corresponding ECHConfig </del>  structure; enc contains the encapsulated key as output by SetupBaseS; and but MAY be held constant for successive connections to the same server in the same session. <del> Set the "config_digest" 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. ClientEncryptedCH.encrypted_ch.  This matching procedure should be done using one of the following two checks: <del> Compare ClientEncryptedCH.config_digest against digests of known </del>  ECHConfig and choose the one that matches. Use trial decryption of ClientEncryptedCH.encrypted_ch with known ECHConfig and choose the one that succeeds. Some uses of ECH, such as local discovery mode, may omit the <del> ClientEncryptedCH.config_digest since it can be used as a tracking </del>  vector.  In such cases, trial decryption should be used for matching ClientEncryptedCH to known ECHConfig.  Unless specified by the application using (D)TLS or externally configured on both sides, plaintext SNI name.  This restriction allows a client to reject resumptions in auth-public-name. <del> Note that an unrecognized ClientEncryptedCH.config_digest value may be a GREASE ECH extension (see grease-extensions), so it is necessary for servers to proceed with the connection and rely on the client to </del>  abort if ECH was required.  In particular, the unrecognized value alone does not indicate a misconfigured ECH advertisement (misconfiguration).  Instead, servers can measure occurrences of the maximize the size of the anonymity set where possible.  This means client-facing servers should use the same ECHConfig for as many hosts as possible.  An attacker can distinguish two hosts that have <del> different ECHConfig values based on the ClientEncryptedCH.config_digest value.  This also means public information in a TLS handshake is also consistent across hosts.  For example, if a client-facing server services many backend origin hosts, only one of which supports some cipher suite, it may be possible to identify that host based on the contents of unencrypted handshake messages. </del>  10.2. 10.4. <del> Optional record digests may be useful in scenarios where clients and client-facing servers do not want to reveal information about the client-facing server in the "encrypted_client_hello" extension.  In such settings, clients send either an empty config_digest or a randomly generated config_digest in the ClientEncryptedCH.  (The </del>  precise implementation choice for this mechanism is out of scope for this document.)  Servers in these settings must perform trial decryption since they cannot identify the client's chosen ECH key <del> using the config_digest value.  As a result, support for optional record digests may exacerbate DoS attacks.  Specifically, an adversary may send malicious ClientHello messages, i.e., those which will not decrypt with any known ECH key, in order to force wasteful decryption.  Servers that support this feature should, for example, implement some form of rate limiting mechanism to limit the damage caused by such attacks. </del>  10.5.

Also, rename configdigest to configid to avoid confusion with cryptographic hash digests. cc NAME NAME NAME
It's bound by the size o the HPKE KDF output size, which is unlikely to be larger than 32B. Let's make this:
We should also add a requirement for the HPKE ciphersuite. E.g., 'Nh <= 2^8`.
(Careful with fenceposts: .) If config_digest is the output of a KDF, why could you not fix the length to something that is enough to avoid collisions (16 bytes seems ample for this) and not worry about Nh at all?
I think it makes sense to let the KDF dictate the security level, i.e., the collision probability of the config_digest.
I would argue that collision probability here doesn't contribute to security. If a config is misidentified, the result will be failure, not a reduction in security. A failure either because the group doesn't match or because peers end up disagreeing on keys.
True that it's an operational consideration rather than a security one. I still think it makes sense to let the length of be dictated by the KDF, since whether truncation is an operational risk depends on the KDF.
I assume here that a KDF is capable of producing uniform random bytes of arbitrary length, meaning that truncation isn't a factor. That's what is for, right?
That assumption holds if the input has sufficient entropy. Since the public key is public, there's no entropy from the point of view of an adversary who wants to distinguish the output from random.
(To be clear, I think truncating to something reasonable, like 16 bytes, is low risk. I would be fine with doing this.)

match a value provided in the corresponding "ECHConfig.cipher_suites" list.  <ins> The configuration identifier, equal to "Expand(Extract("", config), "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. </ins> The HPKE encapsulated key, used by servers to decrypt the corresponding "encrypted_ch" field. cipher_suite contains the client's chosen HpkeCipherSuite;  <ins> config_id contains the identifier of the corresponding ECHConfig </ins> structure; enc contains the encapsulated key as output by SetupBaseS; and 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 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.) </ins> Set the "enc" field to a randomly-generated valid encapsulated public key output by the HPKE KEM. ClientEncryptedCH.encrypted_ch.  This matching procedure should be done using one of the following two checks:  <ins> Compare ClientEncryptedCH.config_id against identifiers of known </ins> ECHConfig and choose the one that matches. Use trial decryption of ClientEncryptedCH.encrypted_ch with known ECHConfig and choose the one that succeeds. Some uses of ECH, such as local discovery mode, may omit the  <ins> ClientEncryptedCH.config_id since it can be used as a tracking </ins> vector.  In such cases, trial decryption should be used for matching ClientEncryptedCH to known ECHConfig.  Unless specified by the application using (D)TLS or externally configured on both sides, plaintext SNI name.  This restriction allows a client to reject resumptions in auth-public-name.  <ins> Note that an unrecognized ClientEncryptedCH.config_id value may be a GREASE ECH extension (see grease-extensions), so it is necessary for servers to proceed with the connection and rely on the client to </ins> abort if ECH was required.  In particular, the unrecognized value alone does not indicate a misconfigured ECH advertisement (misconfiguration).  Instead, servers can measure occurrences of the maximize the size of the anonymity set where possible.  This means client-facing servers should use the same ECHConfig for as many hosts as possible.  An attacker can distinguish two hosts that have  <ins> different ECHConfig values based on the ClientEncryptedCH.config_id value.  This also means public information in a TLS handshake is also consistent across hosts.  For example, if a client-facing server services many backend origin hosts, only one of which supports some cipher suite, it may be possible to identify that host based on the contents of unencrypted handshake messages. </ins> 10.2. 10.4.  <ins> Optional configuration identifiers may be useful in scenarios where clients and client-facing servers do not want to reveal information about the client-facing server in the "encrypted_client_hello" extension.  In such settings, clients send either an empty config_id or a randomly generated config_id in the ClientEncryptedCH.  (The </ins> precise implementation choice for this mechanism is out of scope for this document.)  Servers in these settings must perform trial decryption since they cannot identify the client's chosen ECH key  <ins> using the config_id value.  As a result, support for optional configuration identifiers may exacerbate DoS attacks.  Specifically, an adversary may send malicious ClientHello messages, i.e., those which will not decrypt with any known ECH key, in order to force wasteful decryption.  Servers that support this feature should, for example, implement some form of rate limiting mechanism to limit the damage caused by such attacks. </ins> 10.5.


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

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!

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


The "ECHConfig" structure contains the following fields: <del> The version of ECH for which this configuration is used.  The version of this document SHALL be "0xff07".  (Future drafts and the final version MUST define a version that has not already been assigned.)  Clients MUST ignore any "ECHConfig" structure with a version they do not support. </del>  The length, in bytes, of the next field. <del> An opaque byte string whose contents depend on the version of the structure.  For this specification, the contents are an "ECHConfigContents" structure. </del>  The "ECHConfigContents" structure contains the following fields: IANA is requested to create the following two entries in the existing registry for ExtensionType (defined in RFC8446): <del> encrypted_client_hello(0xff02), with "TLS 1.3" column values being </del>  set to "CH, EE", and "Recommended" column being set to "Yes". ech_nonce(0xff03), with the "TLS 1.3" column values being set to 12. Any future information or hints that influence the outer ClientHello <del> SHOULD be specified as ECHConfig extensions, or in an entirely new version of ECHConfig.  This is primarily because the outer ClientHello exists only in support of ECH.  Namely, it is both an envelope for the encrypted inner ClientHello and enabler for authenticated key mismatch signals (see server-behavior).  In contrast, the inner ClientHello is the true ClientHello used upon ECH negotiation. </del>

Addresses URL
The ECH version is negotiated as follows: the server indicates which versions of ECH it supports by advertising a configuration for each supported version, and the client selects the version by selecting a configuration. The selected configuration is currently indicated by . The structure is defined as follows: Because the negotiated version is determined by the configuration, future versions of ECH that change the structure of --- in particular, the way the selected configuration is indicated--- will break backwards compatibility. Therefore, I would like to propose adding the ECH version to to this structure.
Oh nice catch. I agree that the versioning scheme doesn't work here, though our experience adding extension points inside extension points has not been good. Implementations keep messing them up and we lose the extension point anyway. ("Have one joint and keep it well-oiled.") Do we actually need the versioning scheme after the draft is done? If it weren't for the SVCB code point, I would say we should simply pick different extension code points for each deployed draft. But perhaps we should just do that for the SVCB code point too? If we did that, would we need versions in the first place?
Well, strictly speaking they will only do so if they break the first two fields, right? Because the rest can be conditioned on the config_digest.
True, but to NAME point, I wonder if versioning ECHConfigContents is necessary. Would it be sufficient to update the code point for the draft whenever this changes?
Unless we advertise the version here, we're committing to the first two fields being the same in future versions. Unless we anticipate these changing, let's stick with the status quo.
Each HPKE suite has a fixed-length key encapsulation (), so the length field of is redundant. If we concatenate and into a single opaque field, that might be more future-proof.
I don't see how that helps, since the fields that can't change are ciphersuties and configid.
By way of addressing this issue, I've created a PR that "locks" ECHConfig.version to the extension code point. URL This provides an unambiguous signal of which version of ECH is selected by the client.
I'm not sure the choice of code point (ff08) is available. I picked it because it reflects the next draft (08).
With merged, I wonder if the ESNIKeys struct should just be renamed to something not specific to ESNI, but something more like "generic TLS config in DNS" so that it can be more cleanly reused by unrelated features. "ServerConfiguration" comes to mind from the early TLS 1.3 days.
If we were to re-use these keys for other purposes, that seems fine. However, I'm not sure we want to do that. Semi-static keys, for example, should ideally be separate.
The keys yes, but I imagine the extensions field can hold "arbitrary" data, so while the keys would be ESNI-specific, you could also include extensions that have nothing to do with ESNI.
Thinking about this some more, this would also require changing the "_esni." prefix to something else. In any case I can see how this might be out of scope for this spec. In the end it's a matter of deciding whether future TLS extensions that need similar DNS records, should be able to reuse the same structure (so no additional TXT record with its own prefix would be required), or if they should define their own structure (and have a separate TXT record). I'm happy to close this if it goes too much out of scope.
Right, I think it's verging on being out of scope. What do others think? NAME NAME
My preference goes to keeping the name as-is, considering the fact that the role of ESNIKeys is (at least for the moment) to negotiate properties between the client and the fronting server (not the hidden server), and that the only property we need to negotiate between the two is the information necessary for ESNI protection. It is true that the DNS record can covey properties related to the hidden server (as you know, I've argued for using it to carry the server certificate chain). But IMO that's a change of concept, and I prefer keeping the concept simple for the time being.
Ok.

The "ECHConfig" structure contains the following fields:  <ins> The version of ECH for which this configuration is used. Beginning with draft-08, the version is the same as the code point for the "encrypted_client_hello" extension.  Clients MUST ignore any "ECHConfig" structure with a version they do not support. </ins> The length, in bytes, of the next field.  <ins> An opaque byte string whose contents depend on the version.  For this specification, the contents are an "ECHConfigContents" structure. </ins> The "ECHConfigContents" structure contains the following fields: IANA is requested to create the following two entries in the existing registry for ExtensionType (defined in RFC8446):  <ins> encrypted_client_hello(0xff08), with "TLS 1.3" column values being </ins> set to "CH, EE", and "Recommended" column being set to "Yes". ech_nonce(0xff03), with the "TLS 1.3" column values being set to 12. Any future information or hints that influence the outer ClientHello  <ins> SHOULD be specified as ECHConfig extensions.  This is primarily because the outer ClientHello exists only in support of ECH.  Namely, it is both an envelope for the encrypted inner ClientHello and enabler for authenticated key mismatch signals (see server-behavior). In contrast, the inner ClientHello is the true ClientHello used upon ECH negotiation. </ins>