server.  It SHOULD retry the handshake with a new transport connection, using the retry configurations supplied by the server. The retry configurations may only be applied to the retry connection. <del> The client MUST continue to use the previously-advertised configurations for subsequent connections.  This avoids introducing pinning concerns or a tracking vector, should a malicious server present client-specific retry configurations in order to identify the client in a subsequent ECH handshake. </del>  If none of the values provided in "retry_configs" contains a supported version, the client can regard ECH as securely disabled by

When retry-configs are defined in section 5 we say they are for "subsequent connection attempts" but in 6.1.6 we have a MUST NOT for using those for "subsequent connections." That seems inconsistent and maybe even wrong if one thinks about how e.g. wget can crawl web pages, or how applications might use libcurl, so this change tries to fix that. Not sure if short duration is the right way to phrase it though.

server.  It SHOULD retry the handshake with a new transport connection, using the retry configurations supplied by the server. The retry configurations may only be applied to the retry connection.  <ins> The client MUST NOT use retry configurations for connections beyond the retry.  This avoids introducing pinning concerns or a tracking vector, should a malicious server present client-specific retry configurations in order to identify the client in a subsequent ECH handshake. </ins> If none of the values provided in "retry_configs" contains a supported version, the client can regard ECH as securely disabled by


ECHClientHello.config_id since it can be used as a tracking vector. In such cases, the second method should be used for matching the ECHClientHello to a known ECHConfig.  See optional-configs.  Unless <del> specified by the application using (D)TLS or externally configured on both sides, implementations MUST use the first method. </del>  The server then iterates over the candidate ECHConfig values, attempting to decrypt the "encrypted_client_hello" extension:

said to not use trial decryption unless "externally configured on both sides" but there's no way to know that, so I think the "on both sides" ought be deleted unless something else was intended.

ECHClientHello.config_id since it can be used as a tracking vector. In such cases, the second method should be used for matching the ECHClientHello to a known ECHConfig.  See optional-configs.  Unless  <ins> specified by the application using (D)TLS or externally configured, implementations MUST use the first method. </ins> The server then iterates over the candidate ECHConfig values, attempting to decrypt the "encrypted_client_hello" extension:


5.1. <del> Some TLS 1.3 extensions can be quite large, thus repeating them in the ClientHelloInner and ClientHelloOuter can lead to an excessive overall size.  One pathological example is "key_share" with post- quantum algorithms.  To reduce the impact of duplicated extensions, the client may use the "ech_outer_extensions" extension. OuterExtensions consists of one or more ExtensionType values, each of which reference an extension in ClientHelloOuter.  The extensions in OuterExtensions MUST appear in ClientHelloOuter in the same relative order, however, there is no requirement that they be continguous. For example, OuterExtensions may contain extensions A, B, C, while ClientHelloOuter contains extensions A, D, B, C, E, F. The "ech_outer_extensions" extension is only used for compressing the ClientHelloInner.  It can only be included in EncodedClientHelloInner, and MUST NOT be sent in either ClientHelloOuter or ClientHelloInner. When sending ClientHello, the client first computes ClientHelloInner, including any PSK binders.  It then computes a new value, the EncodedClientHelloInner, which is the following structure: </del>  The "client_hello" field is computed by first making a copy of ClientHelloInner and setting the "legacy_session_id" field to the Handshake structure's four byte header.  The "zeros" field MUST be all zeroes. <del> The client then MAY substitute extensions which it knows will be duplicated in ClientHelloOuter.  To do so, the client removes and replaces extensions from EncodedClientHelloInner with a single "ech_outer_extensions" extension.  Removed extensions MUST be ordered consecutively in ClientHelloInner.  The list of outer extensions, OuterExtensions, includes those which were removed from EncodedClientHelloInner, in the order in which they were removed. </del>  Finally, the client pads the message by setting the "zeros" field to a byte string whose contents are all zeros and whose length is the

Inspired by NAME . We can go further and just completely define EncodedClientHelloInner first.

5.1.  <ins> Before encrypting, the client pads and optionally compresses ClientHelloInner into a EncodedClientHelloInner structure, defined below: </ins> The "client_hello" field is computed by first making a copy of ClientHelloInner and setting the "legacy_session_id" field to the Handshake structure's four byte header.  The "zeros" field MUST be all zeroes.  <ins> Repeating large extensions, such as "key_share" with post-quantum algorithms, between ClientHelloInner and ClientHelloOuter can lead to excessive size.  To reduce the size impact, the client MAY substitute extensions which it knows will be duplicated in ClientHelloOuter.  It does so by removing and replacing extensions from EncodedClientHelloInner with a single "ech_outer_extensions" extension, defined as follows: OuterExtensions contains the removed ExtensionType values.  Each value references the matching extension in ClientHelloOuter.  The values MUST be ordered contiguously in ClientHelloInner, and the "ech_outer_extensions" extension MUST be inserted in the corresponding position in EncodedClientHelloInner.  Additionally, the extensions MUST appear in ClientHelloOuter in the same relative order.  However, there is no requirement that they be contiguous. For example, OuterExtensions may contain extensions A, B, C, while ClientHelloOuter contains extensions A, D, B, C, E, F. The "ech_outer_extensions" extension can only be included in EncodedClientHelloInner, and MUST NOT appear in either ClientHelloOuter or ClientHelloInner. </ins> Finally, the client pads the message by setting the "zeros" field to a byte string whose contents are all zeros and whose length is the


An ECHConfigList structure containing one or more ECHConfig structures, in decreasing order of preference, to be used by the <del> client in subsequent connection attempts.  These are known as the </del>  server's "retry configurations". Finally, when the client offers the "encrypted_client_hello", if the

…with reference to Section 6.1.6 See also the discussion on tlswg/draft-ietf-tls-esni.

An ECHConfigList structure containing one or more ECHConfig structures, in decreasing order of preference, to be used by the  <ins> client as described in rejected-ech.  These are known as the </ins> server's "retry configurations". Finally, when the client offers the "encrypted_client_hello", if the


authenticating for ECHConfig.contents.public_name as described in auth-public-name.  If authentication or the handshake fails, the client MUST return a failure to the calling application.  It MUST NOT <del> use the retry configurations. Otherwise, if both authentication and the handshake complete successfully, the client MUST abort the connection with an "ech_required" alert.  It then processes the "retry_configs" field from the server's "encrypted_client_hello" extension. If at least one of the values contains a version supported by the client, it can regard the ECH keys as securely replaced by the server.  It SHOULD retry the handshake with a new transport connection, using the retry configurations supplied by the server. The retry configurations may only be applied to the retry connection. The client MUST NOT use retry configurations for connections beyond the retry.  This avoids introducing pinning concerns or a tracking vector, should a malicious server present client-specific retry configurations in order to identify the client in a subsequent ECH handshake. </del>  If none of the values provided in "retry_configs" contains a <del> supported version, the client can regard ECH as securely disabled by the server.  As below, it SHOULD then retry the handshake with a new transport connection and ECH disabled. If the field contains any other value, the client MUST abort the connection with an "illegal_parameter" alert. If the server negotiates an earlier version of TLS, or if it does not provide an "encrypted_client_hello" extension in EncryptedExtensions, the client proceeds with the handshake, authenticating for ECHConfig.contents.public_name as described in auth-public-name.  If an earlier version was negotiated, the client MUST NOT enable the False Start optimization RFC7918 for this handshake.  If authentication or the handshake fails, the client MUST return a failure to the calling application.  It MUST NOT treat this as a secure signal to disable ECH. Otherwise, when the handshake completes successfully with the public name authenticated, the client MUST abort the connection with an "ech_required" alert.  The client can then regard ECH as securely 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 </del>  "encrypted_client_hello" extension.  If the client does not retry in either scenario, it MUST report an error to the calling application.

…eliminate redundancies Avoid restatement by relying on the itemization of conditions that constitute ECH rejection given in 6.1.4. The prior text described aborting the connection with an alert, then processing retryconfigs, which processing could then lead to additional instructions to abort the (aborted) connection with an alert (and possibly a different alert than at first). Remove the use of the "illegalparameter" alert in the case of "if the field contains any other value", which apparently referred to the retryconfig version fields. First, this conflicts with the requirement that the connection be aborted with echrequired. Second, it's not clear what "any other value" is supposed to mean as this determination follows determining whether the members of the set of values are either "supported" or "unsupported". Perhaps it meant a value not considered valid as of the specification version the client was implemented according to? Certainly, in the case where there is at least one supported version found but also at least one such invalid value, it would not be desriable to use an illegalparameter alert, as this situation will naturally arise when servers deploying a new ECH version alongside an existing one serve clients that have not yet been upgraded. It's also unclear what value client discrimination of unsupported-recognized-as-valid from unsupported-not-recognized-as-valid provides. Linearize the presentation.Fix the reference to the obsolete "echretry_request". (See also tlswg/draft-ietf-tls-esni).

authenticating for ECHConfig.contents.public_name as described in auth-public-name.  If authentication or the handshake fails, the client MUST return a failure to the calling application.  It MUST NOT  <ins> use the retry configurations.  It MUST NOT treat this as a secure signal to disable ECH. If the server supplied an "encrypted_client_hello" extension in its EncryptedExtensions message, the client MUST check that it is syntactically valid and the client MUST abort the connection with a "decode_error" alert otherwise.  If an earlier TLS version was negotiated, the client MUST NOT enable the False Start optimization RFC7918 for this handshake.  If both authentication and the handshake complete successfully, the client MUST perform the processing described below then abort the connection with an "ech_required" alert before sending any application data to the server. If the server provided "retry_configs" and if at least one of the values contains a version supported by the client, the client can regard the ECH keys as securely replaced by the server.  It SHOULD retry the handshake with a new transport connection, using the retry configurations supplied by the server.  The retry configurations may only be applied to the retry connection.  The client MUST NOT use retry configurations for connections beyond the retry.  This avoids introducing pinning concerns or a tracking vector, should a malicious server present client-specific retry configurations in order to identify the client in a subsequent ECH handshake. </ins> If none of the values provided in "retry_configs" contains a  <ins> supported version, or an earlier TLS version was negotiated, the client can regard ECH as securely disabled by the server, and it SHOULD retry the handshake with a new transport connection and ECH disabled. Clients SHOULD implement a limit on retries caused by receipt of "retry_configs" or servers which do not acknowledge the </ins> "encrypted_client_hello" extension.  If the client does not retry in either scenario, it MUST report an error to the calling application.


interpretation rules apply: extensions MAY appear in any order, but there MUST NOT be more than one extension of the same type in the extensions block.  An extension can be tagged as mandatory by using <del> an extension type codepoint with the high order bit set to 1.  A client that receives a mandatory extension they do not understand MUST reject the "ECHConfig" content. </del>  Clients MUST parse the extension list and check for unsupported mandatory extensions.  If an unsupported mandatory extension is

I can see how that sentence is redundant, but it seems like it would be helpful to implementers.

interpretation rules apply: extensions MAY appear in any order, but there MUST NOT be more than one extension of the same type in the extensions block.  An extension can be tagged as mandatory by using  <ins> an extension type codepoint with the high order bit set to 1. </ins> Clients MUST parse the extension list and check for unsupported mandatory extensions.  If an unsupported mandatory extension is


Any referenced extension is missing in ClientHelloOuter. <del> "encrypted_client_hello" appears in OuterExtensions. </del>  The extensions in ClientHelloOuter corresponding to those in OuterExtensions do not occur in the same order.  Implementations SHOULD bound the time to compute a ClientHelloInner proportionally to the ClientHelloOuter size.  If the cost is disproportionately large, a malicious client could exploit this in a extension, which left the overall ClientHello vulnerable to an analogue of this attack.  11. 11.1.

If OuterExtensions contains duplicate references to the same extension in the ClientHelloOuter, a draft-compliant implementation may create a ClientHelloInner many times larger than the received ClientHelloOuter. In split mode, this allows for network DoS via packet amplification. In shared mode, this could lead to memory or CPU exhaustion. This PR requires implementions to reject OuterExtensions which reference the same extension more than once. This can be achieved in practice by using the algorithm suggested in Appendix B.
This used to be in there and then was removed in URL (NAME It's a little weird because the algorithm in the appendix does not actually check for duplicate extensions, merely that you never try to copy the same extension twice. It only implicitly checks for duplicate extensions if you had previously checked the ClientHelloOuter for duplicate extensions. How we want to reflect this in the spec, I'm not sure. Our implementation always checks for duplications, so I don't have much of a horse in this race. But I think many don't (it requires a sort or so), and I do feel that you should be able to apply the appendix and not think about it so hard. The current text, without this PR, says it's not a strict requirement on the parsing side, but that you should avoid DoS. I think those do imply you need to do something here, but it is unfortunate that it doesn't call out this case. I suppose you could argue that a duplicate is violating the order rule.
Right, so, the simplest thing seems to just be to address the case directly, no?
Well, I think the question is whether this is compliant: ClientHelloOuter parser does not reject duplicate unknown extensions. (I don't believe it's required to?) ClientHelloInner decoder just implements the algorithm in the appendix. This has no DoS issues, but will accept a duplicate if, say: ClientHelloOuter {A, B, C, A} ClientHelloInner { outer_extensions { A, A } } This'll copy the first A and then the second A. There are no DoS problems (the same extension is never copied twice), but it does not always reject duplicates. Is that compliant?
Putting compliance aside, let me flip this around -- what is the benefit of allowing duplicates? Rejecting them seems incredibly trivial.
Slightly cooler than more obvious covert channels? :-) Yep. I think it's fine either left alone or specified. The only reason I can think of to specify it is that a client could test whether or not a server does reject. If adding new text just make it a MUST requirement, no need to say HOWTO. Cheers, S.
I'd be fine with a change here, but let's make sure the algorithm in the appendix actually checks for duplicates.
Less code, for simpler implementations that don't want to pay for things they don't need? I do not believe RFC8446 requires that you check for it. Rejecting duplicates of extensions you know about is easy (it's a bitmask). Rejecting them of extensions you don't know about requires a sort, or building a hash table or so. We do this, but I've not seen any other implementation that does it. Right, that's the thing I am not okay with. The algorithm in the appendix is duplicate-check-preserving, and it does no one any good to check for duplicates in the algorithm: If you reject duplicates in your ClientHello parser, implement the appendix and it'll enforce there are no duplicates. If you don't reject duplicates in your ClientHello parser, implement the appendix and you will avoid DoS but potentially have some duplicates. But apparently you were okay with that before ECH, so that's not really any worse. If you don't reject duplicates in your ClientHello parser but do separately reject them in your OuterExtensions decoder, you haven't done anything useful because OuterExtensions can still duplicate with an uncompressed extension. This means the only useful place to apply this check is the ClientHello parser, RFC8446. But we haven't tried to get consensus on mandating ClientHello parsers check for ordered extensions. That would make a bunch of TLS implementations non-compliant. Thus I think the most pragmatic solution is to omit this requirement in ECH.
I think David is correct. OpenSSL IIRC checks for dupes in the existing non-ECH ClientHello handling code. Adding a requirement to barf on dups in outers is ok for me (because it'll just happen), saying HOWTO is likely something I'd ignore as you're unlikely to stumble on the exact way that OpenSSL does it.
I agree with the decision tree, but I don't find this a compelling reason to not prohibit them. We're already changing stacks to accommodate ECH. (As a side question: has anyone really looked at what can happen if certain TLS extensions are repeated?)
NAME given the analysis above, how about we close this an open an issue against rfc8446bis to track extension duplication detection/rejection?
I guess it depends on what you do. The duplicate will be present in the transcript, so... If the server isn't parsing the extension, I expect it doesn't really do anything. If the server is parsing the extension, it's pretty easy to check for duplicates. Supposing it doesn't... If the server ignores one of the duplicates, then we're just pretending that's the interpretation of duplicate extensions. Seems a coherent enough interpretation, if silly. If the server gets confused and mixes the fields together, then it depends on whether that gets the server in a state that it couldn't have gotten into before. If not, no harm. If it does, that novel state might do something weird and untested. If you've got two halves of ClientHello processing and the two have different, non-rejecting interpretations, that may also cause some mishaps. But that sort of thing is a little dubious in TLS anyway given how end-to-end all our negotiations are. Pragmatically, I think rejecting duplicate extensions you already know how to parse is definitely a good idea. It costs next to nothing because you just maintain a bitmask, and reduces some risk. Doing it for extensions you don't recognize is a bit more fuss. Given a time machine, I think we should have mandated all extensions be sorted, so duplicate-checking is easy. Too late now. :-(
This is the scenario that concerns me most, sort of like checking for certain extensions on CH1 in HRR but not on CH2. In any case, I think it's safe to say this is external to ECH, as much as I would prefer we just do it here.
Most of the discussion has veered away from the point of this PR, unfortunately perhaps lead astray by the renaming of the PR. All the discussion about duplicate extensions (or not) in TLS1.3 is not relevant to the property we need to enforce. The intended goal is to ensure that after decompression, the resulting ClientHelloInner must be equal or smaller in size than the received ClientHelloOuter. I believe this is an important security property which must be enforced by ECH. Otherwise, an attacker can send small CHOs to the client-facing server, which then must deliver CHIs potentially >1000x larger to the backend server. Regarding the phrasing of the requirement: This does not require implementations to reject duplicate extensions in CHs and as NAME noted, whether they do or not doesn't matter, as this requirement still ensures the correct security property. If you remove this requirement, regardless of whether duplicate extensions are tolerated, there's an amplification attack. I did consider two alternative formulations Encoding the size requirement directly in the RFC, or Requiring that no CHO extension be included in in the CHI inner more than once. The first option puts the burden entirely on the implementor and seems prone to mistakes. The second seems hard to make precise. Hence this formulation.
Ohhhh! I think I see. Yeah, that is actually slightly different from what we removed in . Sorry, I did not notice this subtlety. Let me try to restate this to make sure we're on the same page: removed the sentence "OuterExtensions contains duplicate values", which means that if the same extension type appears twice in OuterExtensions, the server is obligated to reject. This PR, however, says "Any extension is referenced in OuterExtensions more than once", which one could interpret to not mean the extension type, but the extension itself. And so we're saying that you cannot copy a given (as in the extensiontype/extensiondata tuple) twice. Is that the idea? If so, I like that. It handily punts the RFC8446 problem. :-) I'm not sure how obviously this subtlety comes off in the text, but I don't have a better idea either.
I was looking through previous PRs and it looks like this has been discussed before: highlighted this issue. fixed the issue. re-introduced it. I think it makes sense to add NAME paragraph on the attack to the security considerations section to explain the requirements around decompression better.
Seems OK to me. Looks great

Any referenced extension is missing in ClientHelloOuter.  <ins> Any extension is referenced in OuterExtensions more than once. "encrypted_client_hello" is referenced in OuterExtensions. </ins> The extensions in ClientHelloOuter corresponding to those in OuterExtensions do not occur in the same order. <ins> These requirements prevent an attacker from performing a packet amplification attack, by crafting a ClientHelloOuter which decompresses to a much larger ClientHelloInner.  This is discussed further in decompression-amp. </ins> Implementations SHOULD bound the time to compute a ClientHelloInner proportionally to the ClientHelloOuter size.  If the cost is disproportionately large, a malicious client could exploit this in a extension, which left the overall ClientHello vulnerable to an analogue of this attack. <ins> 10.11.4. Client-facing servers must decompress EncodedClientHelloInners.  A malicious attacker may craft a packet which takes excessive resources to decompress or may be much larger than the incoming packet: If looking up a ClientHelloOuter extension takes time linear in the number of extensions, the overall decoding process would take O(M*N) time, where M is the number of extensions in ClientHelloOuter and N is the size of OuterExtensions. If the same ClientHelloOuter extension can be copied multiple times, an attacker could cause the client-facing server to construct a large ClientHelloInner by including a large extension in ClientHelloOuter, of length L, and an OuterExtensions list referencing N copies of that extension.  The client-facing server would then use O(N*L) memory in response to O(N+L) bandwidth from the client.  In split-mode, an O(N*L) sized packet would then be transmitted to the backend server. ECH mitigates this attack by requiring that OuterExtensions be referenced in order, that duplicate references be rejected, and by recommending that client-facing servers use a linear scan to perform decompression.  These requirements are detailed in encoding-inner. </ins> 11. 11.1.


The "checksum" field provides protection against transmission errors, including those caused by intermediaries such as a DNS proxy running <del> 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. </del>  Client MAY cache the ESNIKeys for a particular domain based on the TTL of the Resource Record, but SHOULD NOT cache it based on the not_after value, to allow servers to rotate the keys often and <del> improve forward secrecy (due to clock skew on the client, it may not be possible for the server to use a very short key lifetime).  Note that servers will need to retain the decryption key for some time after not_after to deal with clock skew, internal caches, and the like. </del>  Note that the length of this structure MUST NOT exceed 2^16 - 1, as the RDLENGTH is only 16 bits RFC1035.

I'm not sure this is much better. I would changes the "Note that servers..." sentence to something like:
See URL

The "checksum" field provides protection against transmission errors, including those caused by intermediaries such as a DNS proxy running  <ins> 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 servers will need to retain the decryption key for some time after "not_after" to deal with clock skew, internal caches, and the like. </ins> Client MAY cache the ESNIKeys for a particular domain based on the TTL of the Resource Record, but SHOULD NOT cache it based on the not_after value, to allow servers to rotate the keys often and  <ins> improve forward secrecy. </ins> Note that the length of this structure MUST NOT exceed 2^16 - 1, as the RDLENGTH is only 16 bits RFC1035.


tls-sni-encryption provides a description of the problem space and some of the proposed techniques.  One of the more difficult problems is "Do not stick out" (I-D.ietf-tls-sni-encryption; Section 2.4): if <del> only hidden services use SNI encryption, then SNI encryption is a signal that a client is going to a hidden server.  For this reason, much recent work has focused on concealing the fact that SNI is being protected.  Unfortunately, the result often has undesirable performance consequences, incomplete coverage, or both. </del>  The design in this document takes a different approach: it assumes <del> that hidden servers will hide behind a provider (CDN, app server, etc.) which is able to activate encrypted SNI (ESNI) for all of the domains it hosts.  Thus, the use of encrypted SNI does not indicate that the client is attempting to reach a hidden server, but only that it is going to a particular service provider, which the observer could already tell from the IP address. </del>  2. directly to that provider, which has access to the plaintext of the connection. <del> In Split Mode, the provider is _not_ the origin server for hidden domains.  Rather the DNS records for hidden domains point to the </del>  provider, but the provider's server just relays the connection back <del> to the hidden server, which is the true origin server.  The provider </del>  does not have access to the plaintext of the connection.  In principle, the provider might not be the origin for any domains, but as a practical matter, it is probably the origin for a large set of <del> innocuous domains, but is also providing protection for some hidden domains.  Note that the hidden server can be an unmodified TLS 1.3 </del>  server. 3.2. encryption for all the domains for which it serves directly or indirectly (via Split mode).  This document defines a publication mechanism using DNS, but other mechanisms are also possible.  In <del> particular, if some of the clients of a hidden server are </del>  applications rather than Web browsers, those applications might have the public key preconfigured. extension, which contains the true extension encrypted under the provider's public key.  The provider can then decrypt the extension and either terminate the connection (in Shared Mode) or forward it to <del> the hidden server (in Split Mode). </del>  4. (client-facing) SNI.  If present, clients SHOULD use its value in the SNI extension of the subsequent ClientHello. <del> Clients obtain these records by querying DNS for hidden server </del>  domains.  Thus, servers operating in Split Mode SHOULD have DNS <del> configured to return the same A (or AAAA) record for all hidden </del>  servers they service.  This yields an anonymity set of cardinality <del> equal to the number of hidden server domains supported by a given client-facing server.  Thus, even with SNI encryption, an attacker which can enumerate the set of hidden server domains supported by a client-facing server can guess the correct SNI with probability at least 1/K, where K is the size of this hidden server anonymity set. This probability may be increased via traffic analysis or other mechanisms. </del>  The "checksum" field provides protection against transmission errors, including those caused by intermediaries such as a DNS proxy running Upon determining the true SNI, the client-facing server then either serves the connection directly (if in Shared Mode), in which case it executes the steps in the following section, or forwards the TLS <del> connection to the hidden server (if in Split Mode).  In the latter </del>  case, it does not make any changes to the TLS messages, but just blindly forwards them. 5.4. <del> The Hidden Server ignores both the "encrypted_server_name" and the </del>  "server_name" (if any) and completes the handshake as usual.  If in Shared Mode, the server will still know the true SNI, and can use it for certificate selection.  In Split Mode, it may not know the true SNI and so will generally be configured to use a single certificate. communicating-sni describes a mechanism for communicating the true <del> SNI to the hidden server. </del>  <del> Similar to the Shared Mode behavior, the hidden server in Split Mode </del>  SHOULD pad the Certificate message, via padding at the record layer such that its length equals the size of the largest possible Certificate (message) covered by the same ESNI key. [[OPEN ISSUE: Do we want "encrypted_server_name" in EE?  It's clearer communication, but would make it so you could not operate a current <del> TLS 1.3 server as a hidden server.]] </del>  6. 7.2.6. This design permits servers operating in Split Mode to forward <del> connections directly to hidden origin servers, thereby avoiding </del>  unnecessary MiTM attacks. 7.2.7. 7.3. <del> Note that the hidden server has no way of knowing what the SNI was, </del>  but that does not lead to additional privacy exposure because the <del> hidden server also only has one identity.  This does, however, change the situation slightly in that the hidden server might previously have checked SNI and now cannot (and an attacker can route a connection with an encrypted SNI to any hidden server and the TLS </del>  connection will still complete).  However, the client is still responsible for verifying the server's identity in its certificate.

I decided "hidden" was bad. NAME
+1 to the change. I think that this is the right direction.

tls-sni-encryption provides a description of the problem space and some of the proposed techniques.  One of the more difficult problems is "Do not stick out" (I-D.ietf-tls-sni-encryption; Section 2.4): if  <ins> only sensitive/private services use SNI encryption, then SNI encryption is a signal that a client is going to such a service.  For this reason, much recent work has focused on concealing the fact that SNI is being protected.  Unfortunately, the result often has undesirable performance consequences, incomplete coverage, or both. </ins> The design in this document takes a different approach: it assumes  <ins> that private origins will co-locate with or hide behind a provider (CDN, app server, etc.) which is able to activate encrypted SNI (ESNI) for all of the domains it hosts.  Thus, the use of encrypted SNI 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 IP address. </ins> 2. directly to that provider, which has access to the plaintext of the connection.  <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, but the provider's server just relays the connection back  <ins> to the backend server, which is the true origin server.  The provider </ins> does not have access to the plaintext of the connection.  In principle, the provider might not be the origin for any domains, but as a practical matter, it is probably the origin for a large set of  <ins> innocuous domains, but is also providing protection for some private domains.  Note that the backend server can be an unmodified TLS 1.3 </ins> server. 3.2. encryption for all the domains for which it serves directly or indirectly (via Split mode).  This document defines a publication mechanism using DNS, but other mechanisms are also possible.  In  <ins> particular, if some of the clients of a private server are </ins> applications rather than Web browsers, those applications might have the public key preconfigured. extension, which contains the true extension encrypted under the provider's public key.  The provider can then decrypt the extension and either terminate the connection (in Shared Mode) or forward it to  <ins> the backend server (in Split Mode). </ins> 4. (client-facing) SNI.  If present, clients SHOULD use its value in the SNI extension of the subsequent ClientHello.  <ins> Clients obtain these records by querying DNS for ESNI-enabled server </ins> domains.  Thus, servers operating in Split Mode SHOULD have DNS  <ins> configured to return the same A (or AAAA) record for all ESNI-enabled </ins> servers they service.  This yields an anonymity set of cardinality  <ins> equal to the number of ESNI-enabled server domains supported by a given client-facing server.  Thus, even with SNI encryption, an attacker which can enumerate the set of ESNI-enabled domains supported by a client-facing server can guess the correct SNI with probability at least 1/K, where K is the size of this ESNI-enabled server anonymity set.  This 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 Upon determining the true SNI, the client-facing server then either serves the connection directly (if in Shared Mode), in which case it executes the steps in the following section, or forwards the TLS  <ins> connection to the backend server (if in Split Mode).  In the latter </ins> case, it does not make any changes to the TLS messages, but just blindly forwards them. 5.4.  <ins> The backend Server ignores both the "encrypted_server_name" and the </ins> "server_name" (if any) and completes the handshake as usual.  If in Shared Mode, the server will still know the true SNI, and can use it for certificate selection.  In Split Mode, it may not know the true SNI and so will generally be configured to use a single certificate. communicating-sni describes a mechanism for communicating the true  <ins> SNI to the backend server. </ins>  <ins> Similar to the Shared Mode behavior, the backend server in Split Mode </ins> SHOULD pad the Certificate message, via padding at the record layer such that its length equals the size of the largest possible Certificate (message) covered by the same ESNI key. [[OPEN ISSUE: Do we want "encrypted_server_name" in EE?  It's clearer communication, but would make it so you could not operate a current  <ins> TLS 1.3 server as a backend server.]] </ins> 6. 7.2.6. This design permits servers operating in Split Mode to forward  <ins> connections directly to backend origin servers, thereby avoiding </ins> unnecessary MiTM attacks. 7.2.7. 7.3.  <ins> Note that the backend server has no way of knowing what the SNI was, </ins> but that does not lead to additional privacy exposure because the  <ins> 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 </ins> connection will still complete).  However, the client is still responsible for verifying the server's identity in its certificate.


such that its length equals the size of the largest possible Certificate (message) covered by the same ESNI key. <del> [[OPEN ISSUE: Do we want "encrypted_server_name" in EE?  It's clearer communication, but would make it so you could not operate a current TLS 1.3 server as a backend server.]] </del> 6. In general, this mechanism is designed only to be used with servers

As per discussion in URL this would require changes to Split Mode backend servers, and it turns out that even for plain "server_name" this behaviour is pretty much useless.

such that its length equals the size of the largest possible Certificate (message) covered by the same ESNI key.  6. In general, this mechanism is designed only to be used with servers


URL is in wglc and seems likely to become a standards track doc. It establishes a registry for undesrscore scoped names it means a blocking reference to ATTRLEAF, but that doesn't seem like a problem time wise

in the existing registry for ExtensionType (defined in I-D.ietf-tls- tls13), with "TLS 1.3" column values being set to "CH", and "Recommended" column being set to "Yes". <ins> 8.2. IANA is requested to create an entry in the DNS Underscore Global Scoped Entry Registry (defined in I-D.ietf-dnsop-attrleaf) with the "RR Type" column value being set to "TXT", the "_NODE NAME" column value being set to "_esni", and the "Reference" column value being set to this document. </ins>


record for that domain. Clients obtain these records by querying DNS for ESNI-enabled server <del> domains.  Thus, servers operating in Split Mode SHOULD have DNS configured to return the same A (or AAAA) record for all ESNI-enabled servers they service.  This yields an anonymity set of cardinality equal to the number of ESNI-enabled server domains supported by a given client-facing server.  Thus, even with SNI encryption, an attacker which can enumerate the set of ESNI-enabled domains supported by a client-facing server can guess the correct SNI with probability at least 1/K, where K is the size of this ESNI-enabled server anonymity set.  This probability may be increased via traffic analysis or other mechanisms. </del>  The "checksum" field provides protection against transmission errors, including those caused by intermediaries such as a DNS proxy running

NAME please have a look!
The order of DNS queries may matter in systems that use Happy Eyeballs. Also, should TLS connections block until ESNI key queries come back? What if they never come back?
can you elaborate on what you're thinking about wrt he? (assuming the URL plan is off the table)
Basically addressed in .

record for that domain. Clients obtain these records by querying DNS for ESNI-enabled server  <ins> domains.  Clients may initiate these queries in parallel alongside normal A or AAAA queries, and SHOULD block TLS handshakes until they complete, perhaps by timing out. Servers operating in Split Mode SHOULD have DNS configured to return the same A (or AAAA) record for all ESNI-enabled servers they service.  This yields an anonymity set of cardinality equal to the number of ESNI-enabled server domains supported by a given client- facing server.  Thus, even with SNI encryption, an attacker which can enumerate the set of ESNI-enabled domains supported by a client- facing server can guess the correct SNI with probability at least 1/ K, where K is the size of this ESNI-enabled server anonymity set. This 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


yet seen significant (or really any) security analysis.  It should not be used as a basis for building production systems. <del> Although TLS 1.3 I-D.ietf-tls-tls13 encrypts most of the handshake, including the server certificate, there are several other channels that allow an on-path attacker to determine the domain name the client is trying to connect to, including: </del>  Cleartext client DNS queries. A list of extensions that the client can take into consideration when generating a Client Hello message.  The format is defined in <del> 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. </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 The encrypted SNI is carried in an "encrypted_server_name" extension, which contains an EncryptedSNI structure:  A cryptographic hash of the ESNIKeys structure from which the ESNI key was obtained, i.e., from the first byte of "checksum" to the end of the structure.  This hash is computed using the hash function associated with "suite". <del> The cipher suite used to encrypt the SNI. </del> The original ServerNameList from the "server_name" extension, padded and AEAD-encrypted using cipher suite "suite" and with the key generated as described below. 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.  This share is then used for the client's "key_share" extension and will be used to derive both the SNI encryption key and the (EC)DHE shared secret which is used in the TLS key schedule.  This has two important implications: The client MUST only provide one KeyShareEntry </del>  <del> The server is committing to support every group in the ESNIKeys list (see below for server behavior). </del>  <del> The SNI encryption key is computed from the DH shared secret Z as follows: </del>  The client then creates a PaddedServerNameList: The EncryptedSNI.encrypted_sni value is then computed using the usual TLS 1.3 AEAD:  Note: future extensions may end up reusing the server's ESNIKeyShareEntry for other purposes within the same message (e.g., encrypting other values).  Those usages MUST have their own HKDF necessary to prevent downgrade attacks.  [[OPEN ISSUE: We looked at ignoring the extension but concluded this was better.]] <del> 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. </del>  If the length of the "encrypted_server_name" extension is inconsistent with the advertised padding length (plus AEAD If DNS is misconfigured so that a client receives ESNI keys for a server which is not prepared to receive ESNI, then the server will <del> ignore the "encrypted_server_name" extension, as required by I- D.ietf-tls-tls13; Section 4.1.2.  If the servers does not require SNI, it will complete the handshake with its default certificate. Most likely, this will cause a certificate name mismatch and thus handshake failure.  Clients SHOULD not fall back to cleartext SNI, because that allows a network attacker to disclose the SNI.  They MAY attempt to use another server from the DNS results, if one is provided. </del>  6.2. A more serious problem is MITM proxies which do not support this <del> extension.  I-D.ietf-tls-tls13; Section 9.3 requires that such proxies remove any extensions they do not understand.  This will have one of two results when connecting to the client-facing server: </del>  The handshake will fail if the client-facing server requires SNI. 7.2.8. This design has no impact on application layer protocol negotiation. <del> It only affects connection routing, server certificate selection, and </del>  client certificate verification.  Thus, it is compatible with multiple protocols. 8.1. IANA is requested to Create an entry, encrypted_server_name(0xffce), <del> in the existing registry for ExtensionType (defined in I-D.ietf-tls- tls13), with "TLS 1.3" column values being set to "CH", and "Recommended" column being set to "Yes". </del>  8.2.

Replace "It only affects connection routing, server certificate selection, and client certificate verification." with "It may affect connection routing, server certificate selection, and client certificate verification."
URL It is relative to "This design has no impact on application layer protocol negotiation.", so "only" is more explicit.
This was fixed in and I forgot to close.

yet seen significant (or really any) security analysis.  It should not be used as a basis for building production systems.  <ins> Although TLS 1.3 RFC8446 encrypts most of the handshake, including the server certificate, there are several other channels that allow an on-path attacker to determine the domain name the client is trying to connect to, including: </ins> Cleartext client DNS queries. A list of extensions that the client can take into consideration when generating a Client Hello message.  The format is defined in  <ins> RFC8446; 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 The encrypted SNI is carried in an "encrypted_server_name" extension, which contains an EncryptedSNI structure: <ins> The cipher suite used to encrypt the SNI. The KeyShareEntry carrying the client's public ephemeral key shared used to derive the ESNI key. </ins> A cryptographic hash of the ESNIKeys structure from which the ESNI key was obtained, i.e., from the first byte of "checksum" to the end of the structure.  This hash is computed using the hash function associated with "suite".  The original ServerNameList from the "server_name" extension, padded and AEAD-encrypted using cipher suite "suite" and with the key generated as described below. 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 will then be sent to the server in the EncryptedSNI extension and used to derive the SNI encryption key. It does not affect the (EC)DHE shared secret used in the TLS key schedule. </ins>  <ins> Let Z be the DH shared secret derived from a key share in ESNIKeys and the corresponding client share in EncryptedSNI.key_share.  The SNI encryption key is computed from Z as follows: </ins>  <ins> where ESNIContents is as specified below and Hash is the hash function associated with the HKDF instantiation. </ins> The client then creates a PaddedServerNameList: The EncryptedSNI.encrypted_sni value is then computed using the usual TLS 1.3 AEAD: <ins> Including ClientHello.KeyShareClientHello in the AAD of AEAD-Encrypt binds the EncryptedSNI value to the ClientHello and prevents cut-and- paste attacks. </ins> Note: future extensions may end up reusing the server's ESNIKeyShareEntry for other purposes within the same message (e.g., encrypting other values).  Those usages MUST have their own HKDF necessary to prevent downgrade attacks.  [[OPEN ISSUE: We looked at ignoring the extension but concluded this was better.]]  <ins> If the EncryptedSNI.key_share group does not match one in the ESNIKeys.keys, it MUST abort the connection with an "illegal_parameter" alert. </ins> If the length of the "encrypted_server_name" extension is inconsistent with the advertised padding length (plus AEAD If DNS is misconfigured so that a client receives ESNI keys for a server which is not prepared to receive ESNI, then the server will  <ins> ignore the "encrypted_server_name" extension, as required by RFC8446; Section 4.1.2.  If the servers does not require SNI, it will complete the handshake with its default certificate.  Most likely, this will cause a certificate name mismatch and thus handshake failure. Clients SHOULD NOT fall back to cleartext SNI, because that allows a network attacker to disclose the SNI.  They MAY attempt to use another server from the DNS results, if one is provided. </ins> 6.2. A more serious problem is MITM proxies which do not support this  <ins> extension.  RFC8446; Section 9.3 requires that such proxies remove any extensions they do not understand.  This will have one of two results when connecting to the client-facing server: </ins> The handshake will fail if the client-facing server requires SNI. 7.2.8. This design has no impact on application layer protocol negotiation.  <ins> It may affect connection routing, server certificate selection, and </ins> client certificate verification.  Thus, it is compatible with multiple protocols. 8.1. IANA is requested to Create an entry, encrypted_server_name(0xffce),  <ins> in the existing registry for ExtensionType (defined in RFC8446), with "TLS 1.3" column values being set to "CH", and "Recommended" column being set to "Yes". </ins> 8.2.


5. The encrypted SNI is carried in an "encrypted_server_name" extension, <del> which contains an EncryptedSNI structure: </del>  The cipher suite used to encrypt the SNI. padded and AEAD-encrypted using cipher suite "suite" and with the key generated as described below.  5.1. In order to send an encrypted SNI, the client MUST first select one The client then creates a PaddedServerNameList:  This value consists of the serialized ServerNameList from the "server_name" extension, padded with enough zeroes to make the total structure ESNIKeys.padded_length bytes long.  The purpose of the encrypting other values).  Those usages MUST have their own HKDF labels to avoid reuse. <del> [[OPEN ISSUE: If in future you were to reuse these keys for 0-RTT </del>  priming, then you would have to worry about potentially expanding twice of Z_extracted.  We should think about how to harmonize these <del> to make sure that we maintain key separation.  Similarly, if the server uses the same key for ESNI as it does in ServerKeyShare, this is going to involve re-use of Z in some hard to analyze ways.  Of course, this would also involve abandoning PFS.]] </del>  This value is placed in an "encrypted_server_name" extension. innocuous dummy one (this is required for technical conformance with RFC7540; Section 9.2.)  5.2. Upon receiving an "encrypted_server_name" extension, the client- it were the "server_name" extension to finish the handshake.  It SHOULD pad the Certificate message, via padding at the record layer, such that its length equals the size of the largest possible <del> Certificate (message) covered by the same ESNI key. </del>  5.4. <del> The backend Server ignores both the "encrypted_server_name" and the "server_name" (if any) and completes the handshake as usual.  If in Shared Mode, the server will still know the true SNI, and can use it for certificate selection.  In Split Mode, it may not know the true SNI and so will generally be configured to use a single certificate. communicating-sni describes a mechanism for communicating the true SNI to the backend server. Similar to the Shared Mode behavior, the backend server in Split Mode SHOULD pad the Certificate message, via padding at the record layer such that its length equals the size of the largest possible Certificate (message) covered by the same ESNI key. </del>  6. IANA is requested to Create an entry, encrypted_server_name(0xffce), in the existing registry for ExtensionType (defined in RFC8446), with <del> "TLS 1.3" column values being set to "CH", and "Recommended" column being set to "Yes". </del>  8.2.

This is a proposal to address the misrouting and key share replacement attacks identified by Ekr, Karthik, and Benjamin. Please help me improve it!
Some minor nits.

5. The encrypted SNI is carried in an "encrypted_server_name" extension,  <ins> defined as follows: For clients (in ClientHello), this extension contains the following EncryptedSNI structure: </ins> The cipher suite used to encrypt the SNI. padded and AEAD-encrypted using cipher suite "suite" and with the key generated as described below. <ins> For servers (in EncryptedExtensions), this extension contains the following structure: The contents of PaddedServerNameList.nonce.  (See client- behavior.) </ins> 5.1. In order to send an encrypted SNI, the client MUST first select one The client then creates a PaddedServerNameList: <ins> The true SNI. A random 16-octet value to be echoed by the server in the "encrypted_server_name" extension. Zero padding whose length makes the serialized struct length match ESNIKeys.padded_length. </ins> This value consists of the serialized ServerNameList from the "server_name" extension, padded with enough zeroes to make the total structure ESNIKeys.padded_length bytes long.  The purpose of the encrypting other values).  Those usages MUST have their own HKDF labels to avoid reuse.  <ins> [[OPEN ISSUE: If in the future you were to reuse these keys for 0-RTT </ins> priming, then you would have to worry about potentially expanding twice of Z_extracted.  We should think about how to harmonize these  <ins> to make sure that we maintain key separation.]] </ins> This value is placed in an "encrypted_server_name" extension. innocuous dummy one (this is required for technical conformance with RFC7540; Section 9.2.) <ins> If the server does not provide an "encrypted_server_name" extension in EncryptedExtensions, the client MUST abort the connection with an "illegal_parameter" alert.  Moreover, it MUST check that PaddedServerNameList.nonce matches the value of the "encrypted_server_name" extension provided by the server, and otherwise abort the connection with an "illegal_parameter" alert. </ins> 5.2. Upon receiving an "encrypted_server_name" extension, the client- it were the "server_name" extension to finish the handshake.  It SHOULD pad the Certificate message, via padding at the record layer, such that its length equals the size of the largest possible  <ins> Certificate (message) covered by the same ESNI key.  Moreover, the server MUST include the "encrypted_server_name" extension in EncryptedExtensions, and the value of this extension MUST match PaddedServerNameList.nonce. </ins> 5.4.  <ins> In Split Mode, the backend server must know PaddedServerNameList.nonce to echo it back in EncryptedExtensions and complete the handshake.  communicating-sni describes one mechanism for sending both PaddedServerNameList.sni and PaddedServerNameList.nonce to the backend server.  Thus, backend servers function the same as servers operating in Shared mode. </ins> 6. IANA is requested to Create an entry, encrypted_server_name(0xffce), in the existing registry for ExtensionType (defined in RFC8446), with  <ins> "TLS 1.3" column values being set to "CH, EE", and "Recommended" column being set to "Yes". </ins> 8.2.


queries example.com, the ESNI TXT name is _esni.example.com.  Servers SHOULD configure DNS such that, upon querying a domain name with ESNI support, at most one A, AAAA, TXT ESNI, and ALTSVC I-D.schwartz- <del> httpbis-dns-alt-svc Resource Record is returned.  Providing more address options and ESNI keys could lead a client to choose an incorrect key for an address.  Alt-Svc records may be used to inform the client of the plaintext (fronting) SNI Also, servers operating in Fronting Mode SHOULD have DNS configured to return the same A (or AAAA) record for all hidden servers they </del>  service. The Resource Record TTL determines the lifetime of the published ESNI

Instead of a Fronting SNI field, we should just re-use the Alt-Svc record, which already carries an SNI value to be used.

queries example.com, the ESNI TXT name is _esni.example.com.  Servers SHOULD configure DNS such that, upon querying a domain name with ESNI support, at most one A, AAAA, TXT ESNI, and ALTSVC I-D.schwartz-  <ins> httpbis-dns-alt-svc Resource Record is returned.  Alt-Svc records may be used to inform the client of the plaintext (fronting) SNI.  Also, servers operating in Fronting Mode SHOULD have DNS configured to return the same A (or AAAA) record for all hidden servers they </ins> service. The Resource Record TTL determines the lifetime of the published ESNI


SNI Encryption keys can be published in the DNS using the ESNIKeys structure, defined below.  The 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 structure. This structure is placed in the RRData section of a TXT record as a base64-encoded string.  If this encoding exceeds the 255 octet limit of TXT strings, it must be split across multiple concatenated strings <del> as per Section 3.1.3 of RFC4408. </del>  The name of each TXT record MUST match the name composed of _esni and the query domain name.  That is, if a client queries example.com, the

Do we need it.
For ESNIKeys (the DNS record) I do not think we need a version for the following reasons: I think we would prefer adding extensions rather than making incompatible changes we always have the freedom to pick a new prefix (e.g. ) or a new RR type when making incompatible changes an incompatible change will require the client to query for two DNS records anyways For the ESNI extension, I also do not think that we need a version field. When we define an extension sometime in the future, we could the advertise the support as an extension in the ESNIKeys to instruct the clients that support that extension to send a CH extension that uses a different extension ID than the ESNI extension.
A lot of other TXT records have a version in them. It turns out that if you do that you can query for TXT records and get multiple answers back. Then, you pick those with the version you like. That's one query. I'm tempted to suggest that the first octet (or two) be used for a version number. It's cheap.
I'm concerned about how we iterate on these in the field. They're not tied to TLS version, so what if we decide to change the formats, as we are discussing in
a magic number (including version) is a pretty decent idea. right now _esni is in its own namespace but you can imagine someone doing a txt record that applies to every dns response so we would want to disambiguate that as well. take note to update the "concat multiple records" rule if we go down this path..
I think that having version is a good idea in sense that it allows to do experiments before the standardization completes.

SNI Encryption keys can be published in the DNS using the ESNIKeys structure, defined below. <ins> The version of the structure.  For this specification, that value SHALL be 0x0000.  Clients MUST ignore any ESNIKeys structure with a version they do not understand.  [[NOTE: This means that the RFC will presumably have a nonzero value.]] </ins> The 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 structure. This structure is placed in the RRData section of a TXT record as a base64-encoded string.  If this encoding exceeds the 255 octet limit of TXT strings, it must be split across multiple concatenated strings  <ins> as per Section 3.1.3 of RFC4408.  Servers MAY supply multiple ESNIKeys values, either of the same or of different versions.  This allows a server to support multiple versions at once. </ins> The name of each TXT record MUST match the name composed of _esni and the query domain name.  That is, if a client queries example.com, the


4.  A key importer takes as input an EPSK with external identity 'external_identity' and base key 'epsk', as defined in terminology, along with an optional label, and transforms it into a set of PSKs that means ALPN, QUIC transport settings, etc., must be provisioned alongside these EPSKs. <del> 5. </del>  For clarity, the following table specifies PSK importer labels for varying instances of the TLS handshake. <del> 6. </del>  If a client or server wish to deprecate a hash function and no longer use it for TLS 1.3, they may remove this hash function from the set of hashes used during while importing keys.  This does not affect the KDF operation used to derive concrete PSKs. <del> 7. </del>  Recall that TLS 1.2 permits computing the TLS PRF with any hash algorithm and PSK.  Thus, an external PSK may be used with the same does not preclude endpoints from using non-imported PSKs for TLS 1.2. Indeed, this is necessary for incremental deployment. <del> 8. </del>  This is a WIP draft and has not yet seen significant security analysis. <del> 9. </del>  DISCLAIMER: This section contains a sketch of a design for protecting external PSK identities.  It is not meant to be implementable as precompute these obfuscated identities to ease the burden of trial decryption. <del> 10. </del>  This document makes no IANA requests.

Discussed in Singapore; will merge and add security considerations motivation in a followup to .
The document says: Aside from this statement needing update in light of the changes to the binder label, the core statement here isn't expanded upon. The document needs to very clearly say that the PSK ID that is sent in the psk extension is literally the serialized content of .
Fixed in .
This seems harmless, but it is predicated on a need to separate keys that I don't understand. Is there more rationale somewhere?

4. <ins> To prevent PSK Importers from being confused with standard OOB PSKs we change the label used in the computation of the PSK binder key. In TLS 1.3 the PSK binder key computation is defined as follows: We replace the string "ext binder" with "imp binder".  This means the binder key is now computed as follows: This new label differentiates non-imported and imported external PSKs.  Specifically, a client and server will negotiate use of an external PSK if and only if (a) both endpoints import the PSK or (b) neither endpoint imports the PSK.  The "binder_key" is a leaf key. Therefore, changing its computation doesn't affect any other key. 5. </ins> A key importer takes as input an EPSK with external identity 'external_identity' and base key 'epsk', as defined in terminology, along with an optional label, and transforms it into a set of PSKs that means ALPN, QUIC transport settings, etc., must be provisioned alongside these EPSKs.  <ins> 6. </ins> For clarity, the following table specifies PSK importer labels for varying instances of the TLS handshake.  <ins> 7. </ins> If a client or server wish to deprecate a hash function and no longer use it for TLS 1.3, they may remove this hash function from the set of hashes used during while importing keys.  This does not affect the KDF operation used to derive concrete PSKs.  <ins> 8. </ins> Recall that TLS 1.2 permits computing the TLS PRF with any hash algorithm and PSK.  Thus, an external PSK may be used with the same does not preclude endpoints from using non-imported PSKs for TLS 1.2. Indeed, this is necessary for incremental deployment.  <ins> 9. </ins> This is a WIP draft and has not yet seen significant security analysis.  <ins> 10. </ins> DISCLAIMER: This section contains a sketch of a design for protecting external PSK identities.  It is not meant to be implementable as precompute these obfuscated identities to ease the burden of trial decryption.  <ins> 11. </ins> This document makes no IANA requests.


versions of TLS will increase the number of PSKs derived from an external PSK.  A unique and imported PSK (IPSK) with base key 'ipskx' bound to this identity is then computed as follows:

To make external psk importers more featureful it makes sense to take advantage of the HKDF already in use. By providing a field for extra context we can avoid protocol layering attacks. For example this change allows us to securely bind one TLS session to another, providing layered context.
See: URL
This was addressed with the slot via and . Closing!
Thanks! This is much more understandable than the previous PR. :-)LGTM with the suggested name change!

versions of TLS will increase the number of PSKs derived from an external PSK. <ins> ImportedIdentity.context MUST include the context used to derive the EPSK, if any exists.  If the EPSK is a key derived from some other protocol or sequence of protocols, ImportedIdentity.context MUST include a channel binding for the deriving protocols RFC5056.  If any secrets are agreed in earlier protocols they SHOULD be included in ImportedIdentity.context CCB. </ins> A unique and imported PSK (IPSK) with base key 'ipskx' bound to this identity is then computed as follows:


provisioned for specific hash functions. To mitigate these problems, external PSKs can be bound to a specific <del> hash function when used in TLS 1.3, even if they are associated with a different KDF (and hash function) when provisioned.  This document specifies an interface by which external PSKs may be imported for use in a TLS 1.3 connection to achieve this goal.  In particular, it describes how KDF-bound PSKs can be differentiated by different hash algorithms to produce a set of candidate PSKs, each of which are bound to a specific hash function.  This expands what would normally have been a single PSK identity into a set of PSK identities. However, it requires no change to the TLS 1.3 key schedule. </del>  2. 3. <del> Intuitively, key importers mirror the concept of key exporters in TLS in that they diversify a key based on some contextual information before use in a connection.  In contrast to key exporters, wherein differentiation is done via an explicit label and context string, the key importer defined herein uses a label and set of hash algorithms to differentiate an external PSK into one or more PSKs for use. </del>  Imported keys do not require negotiation for use, as a client and server will not agree upon identities if not imported correctly. Thus, importers induce no protocol changes with the exception of expanding the set of PSK identities sent on the wire.  Endpoints may incrementally deploy PSK importer support by offering non-imported <del> keys for TLS versions prior to TLS 1.3.  (Negotiation and use of imported PSKs requires both endpoints support the importer API described herein.) </del>  3.1. External PSK (EPSK): A PSK established or provisioned out-of-band, i.e., not from a TLS connection, which is a tuple of (Base Key, <del> External Identity, KDF).  The associated KDF (and hash function) may be undefined. </del>  Base Key: The secret value of an EPSK. External Identity: The identity of an EPSK. <del> Imported Identity: The identity of a PSK as sent on the wire. </del>  4. A key importer takes as input an EPSK with external identity <del> 'external_identity' and base key 'epsk', as defined in terminology, along with an optional label, and transforms it into a set of PSKs </del>  and imported identities for use in a connection based on supported <del> HashAlgorithms.  In particular, for each supported HashAlgorithm 'hash', the importer constructs an ImportedIdentity structure as follows: [[TODO: An alternative design might combine label and hash into the same field so that future protocols which don't have a notion of HashAlgorithm don't need this field.]] ImportedIdentity.label MUST be bound to the protocol for which the key is imported.  Thus, TLS 1.3 and QUICv1 I-D.ietf-quic-transport MUST use "tls13" as the label.  Similarly, TLS 1.2 and all prior TLS versions should use "tls12" as ImportedIdentity.label, as well as SHA256 as ImportedIdentity.hash.  Note that this means future versions of TLS will increase the number of PSKs derived from an external PSK. </del>  ImportedIdentity.context MUST include the context used to derive the <del> EPSK, if any exists.  If the EPSK is a key derived from some other protocol or sequence of protocols, ImportedIdentity.context MUST include a channel binding for the deriving protocols RFC5056.  If any secrets are agreed in earlier protocols they SHOULD be included in ImportedIdentity.context CCB. </del>  <del> A unique and imported PSK (IPSK) with base key 'ipskx' bound to this identity is then computed as follows: </del>  <del> [[TODO: The length of ipskx MUST match that of the corresponding and supported ciphersuites.]] </del>  The hash function used for HKDF RFC5869 is that which is associated <del> with the external PSK.  It is not bound to ImportedIdentity.hash.  If no hash function is specified, SHA-256 MUST be used.  Differentiating epsk by ImportedIdentity.hash ensures that each imported PSK is only used with at most one hash function, thus satisfying the requirements in RFC8446.  Endpoints MUST import and derive an ipsk for each hash function used by each ciphersuite they support.  For example, importing a key for TLS_AES_128_GCM_SHA256 and TLS_AES_256_GCM_SHA384 would yield two PSKs, one for SHA256 and another for SHA384.  In contrast, if TLS_AES_128_GCM_SHA256 and TLS_CHACHA20_POLY1305_SHA256 are supported, only one derived key is necessary. </del>  The resulting IPSK base key 'ipskx' is then used as the binder key in TLS 1.3 with identity ImportedIdentity.  With knowledge of the <del> supported hash functions, one may import PSKs before the start of a connection. </del>  EPSKs may be imported for early data use if they are bound to protocol settings and configurations that would otherwise be required 5. <del> For clarity, the following table specifies PSK importer labels for varying instances of the TLS handshake. 6. </del> If a client or server wish to deprecate a hash function and no longer <del> use it for TLS 1.3, they may remove this hash function from the set of hashes used during while importing keys.  This does not affect the KDF operation used to derive concrete PSKs. </del>  <del> 7. </del>  Recall that TLS 1.2 permits computing the TLS PRF with any hash <del> algorithm and PSK.  Thus, an external PSK may be used with the same KDF (and underlying HMAC hash algorithm) as TLS 1.3 with importers. However, critically, the derived PSK will not be the same since the importer differentiates the PSK via the identity and hash function. Thus, PSKs imported for TLS 1.3 are distinct from those used in TLS 1.2, and thereby avoid cross-protocol collisions.  Note that this </del>  does not preclude endpoints from using non-imported PSKs for TLS 1.2. Indeed, this is necessary for incremental deployment.  8. <del> This is a WIP draft and has not yet seen significant security analysis. </del>  9. <del> DISCLAIMER: This section contains a sketch of a design for protecting external PSK identities.  It is not meant to be implementable as written. </del>  <del> External PSK identities are typically static by design so that endpoints may use them to lookup keying material.  For some systems and use cases, this identity may become a persistent tracking identifier.  One mitigation to this problem is encryption.  Future drafts may specify a way for encrypting PSK identities using a mechanism similar to that of the Encrypted SNI proposal I-D.ietf-tls- esni.  Another approach is to replace the identity with an unpredictable or "obfuscated" value derived from the corresponding PSK.  One such proposal, derived from a design outlined in I-D.ietf- dnssd-privacy, is as follows.  Let ipskx be the imported PSK with identity ImportedIdentity, and N be a unique nonce of length equal to that of ImportedIdentity.hash.  With these values, construct the following "obfuscated" identity: ObfuscatedIdentity.nonce carries N, ObfuscatedIdentity.obfuscated_identity carries HMAC(ipskx, N), where HMAC is computed with ImportedIdentity.hash, and ObfuscatedIdentity.hash is ImportedIdentity.hash. Upon receipt of such an obfuscated identity, a peer must lookup the corresponding PSK by exhaustively trying to compute ObfuscatedIdentity.obfuscated_identity using ObfuscatedIdentity.nonce and each of its known imported PSKs.  If N is chosen in a predictable fashion, e.g., as a timestamp, it may be possible for peers to precompute these obfuscated identities to ease the burden of trial decryption. 10. This document makes no IANA requests. </del>

Also, remove PSK identity encryption sketch. This was underspecified and can always be done in a separate draft if desired. cc NAME
Note, we might want to simply consider merging and into the same field, since we have the registry. That'd save us two bytes. It seems to boil down to whether keeping protocol and KDF separate is better (long term) than merging them. I'm not sure what's the best answer there.
In reviewing the draft, it was not clear that the input PSK was also bound to a hash function, but that the process could produce a derived PSK bound to any hash function. As this is the primary function of the document, that seems problematic. This seems to be the answer to this: I assume that this means KDF rather than hash function throughout (the KDF-bound thing seems right). But it never really comes out and says that:
It's optionally bound to a hash function. In defining external PSKs, the document states that the "associated KDF (and hash function) may be undefined." Do you think it would be more clear to put this in the introduction? (Minimally, as you point out, the introduction should not assume that the KDF-bound PSK as input is not quite right, so that should probably change.) As for the second comment (KDF output), that's not quite the goal. The importer binds input PSKs to a hash via HKDF, not via the TLS KDF. (The TLS 1.2 PRF is not used.) To summarize, my view is that the contract is more or less: optional hash-bound PSK in, hash-bound PSK out. Does that seem incorrect to you?
Hmm, my understanding is that we don't have any support for optional bindings on input. I'm inclined to say that we need stricter constraints on input. Namely that they are bound to a specific KDF and that KDF needs to take a context struct in the form this document describes. Yes, limiting the concrete instantiation of this to HKDF is fine (and the only sensible option). However, if you don't bind the input key more tightly, you will end up shifting the problem you set out to solve one step further out.
I'm not sure I follow anymore! In my view, the problem we aim to solve is one where the external PSK was not provisioned "correctly," i.e., with the right hash (KDF) function in mind. It's just a plain old n-bit key. The importer is designed to bind this key to a specific hash (KDF). If the machinery that produced external PSKs was forced to change to accommodate a KDF, then it seems that machinery could also vend PSKs that do precisely what the importer does. Namely, differentiate by (context, protocol, hash/KDF). Perhaps I'm misunderstanding your point. Can you please clarify? If you're looking for a specific change, could you please make a suggestion?
In TLS 1.2 and earlier, PSKs were not bound to a KDF. Now in TLS 1.3, a PSK is required to be bound to a KDF. I thought the goal was to start with a PSK that is not bound to a KDF and produce a PSK that can be used in the desired context. So, the input is always a "plain old n-bit key" (Chris' words). The output, when the desired context is TLS 1.3, must include the KDF.
This is exactly what the importer does. Do you disagree?
It is not clear to me what the importer does if I want the context to be TLS 1.2.
It changes the info (or proposed field) value and runs the key through a KDF.
Right, I get that, which I think will bind the resulting key to TLS 1.2 but allow any KDF. I think the document should be explicit if that is the case.
Looking at , I think that you want to bind this to the TLS 1.2 KDF differently by using a different KDF identifier. The identifiers you have are only good for TLS 1.3. You probably also want security considerations addressing the fact that the TLS 1.2 has this odd characteristic.
Can you please elaborate on why you think this is necessary? As an example, let's assume I have an external PSK and I want to import it for TLS 1.2 and 1.3, and I only want to support one hash function (or KDF). Let's say that KDF is HKDFSHA256. I would then import with the tuples (0x0303, HKDFSHA256) and (0x0304, HKDF_SHA256), and it seems I'd have the desired separation. In other words, I don't see a technical reason why importing PSKs for TLS 1.2 requires use of the TLS 1.2 KDF (PRF).
I think we are saying the same thing, except that when the protocol version is TLS 1.2 that it will be used with the PRF that is negotiated in the TLS 1.2 handshake. I just think the document should say that explicitly.
This is not what I'm saying! I'm saying do not use the TLS 1.2 PRF. Rather, use HKDF. Can you clarify why you think the TLS 1.2 PRF should be used instead?
There are two thongs that are being confused here. First, the one used to create the protocol-version-specific key. This is the one that you want to always use HKDF. That is fine with me. Second, with TLS 1.3, it is bound to a particular hash function. I am saying that when a key is derived for TLS 1.2 the document should say that it will be used with whatever PRF is negotiated in the TLS handshake. Right now, I do d not see anything about that in the document at all.
Thanks for clarifying! If I use an external PSK for TLS 1.2 today, without any import step, it's already the case that it's used with whatever PRF is negotiated in the handshake. The importer does not change that, and therefore I think doesn't need to say anything about that.
I just think it would be better say it since TLS 1.3 had a binding but TLS 1.2 does not. You now understand my point, so I will leave it to you as author.
Why is this a string and not the protocol version number? It sure would be easier that way.
In writing up , I realize now that the combination of label and hash is what is being used to identify a KDF. I think that you need to allow for the HashAlgorithm to be replaced by something else in future, or for different applications other than TLS. It might pay to instead reduce the tuple of (tls version, hash) to a single identifier that says tls12sha256, tls13hkdf_sha256, which might broaden applicability. That could be crazy-talk though. Either way, it would be good to understand this better in the abstract so as to make a less ad hoc decision about what is being identified and then how.
I agree that the info string should probably just be a version. I imagine there will be a use case where someone wants to, say, import with the label "tls1.3 special sauce". In this case, they could KDF their PSK with the the "special sauce" string and then import with the "tls13" label. And at this point, it might as well be the version number. Let's assume this will be done, and the new field will be called "protocol". So that leaves the question about what to do for the hash algorithm. As you point out, removing the HashAlgorithm dependency would be good for the future. (We have a TODO in the draft to that effect.) So the question then becomes, do we merge whatever this new field is with "protocol," or do we keeep them separate. I don't think we need to merge these for "level matching" or other purposes, so I'm inclined to keep them separate. Thus, how about thee following structure: Where is a uint16 codepoint, and is an HPKE-like identifier [1] for a KDF, such as HKDF-SHA256 or HKDF-SHA512. We could then define the importer with respect to this KDF, rather than baking in a hard dependency on HKDF. NAME NAME what do you think? [1] URL
That seems reasonable - I think that there is some reason to separate target protocol (and version, as a tuple) from the KDF that is used. There's a trick in terms of finding the right level of identification to use for the protocol. I suspect that seeding a registry with 0x0304 and 0x0303 is probably the right way to do this. That has the effect of constraining TLS a little in how it chooses version numbers, but I think that's workable. (As a reminder, in case you thought this was too easy, QUIC uses a 32-bit identifier for versions. We'd need a mapping QUIC. NAME just makes this look simple.)
Currently the context binds the info string (or proposed field) to the version of TLS, not to the calling application or protocol (QUIC).
Do we want to allow the output of the KDF to be anything other that Nh? Right now, L is always URL, which I think i the Nh value associated with the HPKE-like identifier.
Correct! I can't see a reason why we'd want the KDF output to be something different. Do you have a use case in mind?
If we want this to apply to other contexts, like your special sauce above, it is unclear to me that URL is always the right answer.
The point of the importer is to produce a PSK for use in TLS. Why would the context affect the length?
The discussion of is opening this for other protocol contexts too.
NAME NAME I made the proposed change in . Please have a look!
Works for me

provisioned for specific hash functions. To mitigate these problems, external PSKs can be bound to a specific  <ins> KDF and hash function when used in TLS 1.3, even if they are associated with a different hash function when provisioned.  This document specifies an interface by which external PSKs may be imported for use in a TLS 1.3 connection to achieve this goal.  In particular, it describes how KDF-bound PSKs can be differentiated by the target (D)TLS protocol version and KDF for which the PSK will be used.  This produces a set of candidate PSKs, each of which are bound to a specific target protocol and KDF.  This expands what would normally have been a single PSK identity into a set of PSK identities.  However, importantly, it requires no change to the TLS 1.3 key schedule. </ins> 2. 3.  <ins> Key importers mirror the concept of key exporters in TLS in that they diversify a key based on some contextual information before use in a connection.  In contrast to key exporters, wherein differentiation is done via an explicit label and context string, the key importer defined herein uses an optional context string along with a target protocol and KDF identifier to differentiate an external PSK into one or more PSKs for use. </ins> Imported keys do not require negotiation for use, as a client and server will not agree upon identities if not imported correctly. Thus, importers induce no protocol changes with the exception of expanding the set of PSK identities sent on the wire.  Endpoints may incrementally deploy PSK importer support by offering non-imported  <ins> keys for TLS versions prior to TLS 1.3.  Non-imported and imported PSKs are distinct since their identities are different on the wire. See rollout for more details. Clients which import external keys TLS MUST NOT use these keys for any other purpose.  Moreover, each external PSK MUST be associated with at most one hash function. </ins> 3.1. External PSK (EPSK): A PSK established or provisioned out-of-band, i.e., not from a TLS connection, which is a tuple of (Base Key,  <ins> External Identity, Hash). </ins> Base Key: The secret value of an EPSK. External Identity: The identity of an EPSK.  <ins> Target protocol: The protocol for which a PSK is imported for use. Target KDF: The KDF for which a PSK is imported for use. Imported PSK (IPSK): A PSK derived from an EPSK, external identity, optional context string, and target protocol and KDF. Imported Identity: The identity of an Imported PSK as sent on the wire. </ins> 4. A key importer takes as input an EPSK with external identity  <ins> "external_identity" and base key "epsk", as defined in terminology, along with an optional context, and transforms it into a set of PSKs </ins> and imported identities for use in a connection based on supported  <ins> (target) protocols and KDFs.  In particular, for each supported target protocol "target_protocol" and KDF "target_kdf", the importer constructs an ImportedIdentity structure as follows: The list of "target_kdf" values is maintained by IANA as described in IANA.  External PSKs MUST NOT be imported for versions of (D)TLS 1.2 or prior versions.  See rollout for discussion on how imported PSKs for TLS 1.3 and non-imported PSKs for earlier versions co-exist for incremental deployment. </ins> ImportedIdentity.context MUST include the context used to derive the  <ins> EPSK, if any exists.  For example, ImportedIdentity.context may include information about peer roles or identities to mitigate Selfie-style reflection attacks.  See mitigate-selfie for more details.  If the EPSK is a key derived from some other protocol or sequence of protocols, ImportedIdentity.context MUST include a channel binding for the deriving protocols RFC5056. </ins>  <ins> ImportedIdentity.target_protocol MUST be the (D)TLS protocol version for which the PSK is being imported.  For example, TLS 1.3 RFC8446 and QUICv1 QUIC use 0x0304.  Note that this means future versions of TLS will increase the number of PSKs derived from an external PSK. </ins>  <ins> An Imported PSK derived from an EPSK with base key 'epsk' bound to this identity is then computed as follows: L is corresponds to the KDF output length of ImportedIdentity.target_kdf as defined in IANA.  For hash-based KDFs, such as HKDF_SHA256(0x0001), this is the length of the hash function output, i.e., 32 octets.  This is required for the IPSK to be of length suitable for supported ciphersuites. The identity of 'ipskx' as sent on the wire is ImportedIdentity. </ins> The hash function used for HKDF RFC5869 is that which is associated  <ins> with the EPSK.  It is not the hash function associated with ImportedIdentity.target_kdf.  If no hash function is specified, SHA-256 MUST be used.  Diversifying EPSK by ImportedIdentity.target_kdf ensures that an IPSK is only used as input keying material to at most one KDF, thus satisfying the requirements in RFC8446. Endpoints generate a compatible ipskx for each target ciphersuite they offer.  For example, importing a key for TLS_AES_128_GCM_SHA256 and TLS_AES_256_GCM_SHA384 would yield two PSKs, one for HKDF-SHA256 and another for HKDF-SHA384.  In contrast, if TLS_AES_128_GCM_SHA256 and TLS_CHACHA20_POLY1305_SHA256 are supported, only one derived key is necessary. </ins> The resulting IPSK base key 'ipskx' is then used as the binder key in TLS 1.3 with identity ImportedIdentity.  With knowledge of the  <ins> supported KDFs, one may import PSKs before the start of a connection. </ins> EPSKs may be imported for early data use if they are bound to protocol settings and configurations that would otherwise be required 5.  If a client or server wish to deprecate a hash function and no longer  <ins> use it for TLS 1.3, they remove the corresponding KDF from the set of target KDFs used for importing keys.  This does not affect the KDF operation used to derive Imported PSKs. </ins>  <ins> 6. </ins> Recall that TLS 1.2 permits computing the TLS PRF with any hash  <ins> algorithm and PSK.  Thus, an EPSK may be used with the same KDF (and underlying HMAC hash algorithm) as TLS 1.3 with importers.  However, critically, the derived PSK will not be the same since the importer differentiates the PSK via the identity, target protocol, and target KDF.  Thus, PSKs imported for TLS 1.3 are distinct from those used in TLS 1.2, and thereby avoid cross-protocol collisions.  Note that this </ins> does not preclude endpoints from using non-imported PSKs for TLS 1.2. Indeed, this is necessary for incremental deployment. <ins> 7. DISCLAIMER: This is a WIP draft and has not yet seen significant security analysis. </ins> 8.  <ins> External PSK identities are typically static by design so that endpoints may use them to lookup keying material.  However, for some systems and use cases, this identity may become a persistent tracking identifier. </ins> 9.  <ins> This specification introduces a new registry for TLS KDF identifiers and defines the following target KDF values: </ins>  <ins> +-------------+-----+ | Description | Value | +-------------+-----+ | Reserved | 0x0000 | | | | | HKDF_SHA256 | 0x0001 | | | | | HKDF_SHA384 | 0x0002 | +-------------+-----+ New target KDF values are allocated according to the following process: Values in the range 0x0000-0xfeff are assigned via Specification Required RFC8126. Values in the range 0xff00-0xffff are reserved for Private Use RFC8126. </ins>