Omitting the fields and handshake messages required for preserving backwards-compatibility with earlier TLS versions. <del> More compact encodings, omitting unnecessary values. </del>  <del> A template-based specialization mechanism that allows for the creation of application specific versions of TLS that omit unnecessary values. </del>  For the common (EC)DHE handshake with pre-established certificates, cTLS achieves an overhead of 45 bytes over the minimum required by 3.2. <del> The only cTLS records that are sent in plaintext are handshake records (ClientHello and ServerHello/HRR).  The content type is therefore constant (it is always "handshake"), so we instead set the "content_type" field to a fixed cTLS-specific value to distinguish cTLS plaintext records from encrypted records, TLS/DTLS records, and other protocols using the same 5-tuple. The "profile_id" field allows the client and server to agree on which compression profile should be used for this session (see template- based-specialization).  This field MUST be set to zero if and only if no compression profile is used.  Non-zero values are negotiated out of band between the client and server, as part of the specification of the compression profile. [[OPEN ISSUE: The profile_id is needed in the ClientHello to inform the server what compression profile to use.  For a ServerHello this field is not required.  Should we make this field optional?]] Encrypted records use DTLS 1.3 record framing, comprising a configuration octet followed by optional connection ID, sequence number, and length fields. The presence and size of the connection ID field is negotiated as in DTLS. As with DTLS, the length field MAY be omitted by clearing the L bit, which means that the record consumes the entire rest of the data in the lower level transport.  In this case it is not possible to have multiple DTLSCiphertext format records without length fields in the same datagram.  In stream-oriented transports (e.g., TCP), the length field MUST be present.  For use over other transports length information may be inferred from the underlying layer. Normal DTLS does not provide a mechanism for suppressing the sequence number field entirely.  In cases where a sequence number is not required (e.g., when a reliable transport is in use), a cTLS implementation may suppress it by setting the "suppressSequenceNumber" flag in the compression profile being used (see specifying-a-specialization).  When this flag is enabled, the S bit in the configuration octet MUST be cleared. 3.3. The cTLS handshake framing is same as the TLS 1.3 handshake framing, except for two changes: The length field is omitted The HelloRetryRequest message is a true handshake message instead of a specialization of ServerHello. 4. In general, we retain the basic structure of each individual TLS handshake message.  However, the following handshake messages have been modified for space reduction and cleaned up to remove pre TLS 1.3 baggage. 4.1. The cTLS ClientHello is as follows. 4.2. We redefine ServerHello in a similar way: 4.3. </del>  <del> The HelloRetryRequest has the following format: </del>  <del> It is the same as the ServerHello above but without the unnecessary sentinel Random value. 5. The protocol in the previous section is fully general and isomorphic to TLS 1.3; effectively it's just a small cleanup of the wire encoding to match what we might have done starting from scratch.  It achieves some compaction, but only a modest amount. cTLS also includes a mechanism for achieving very high compaction using template-based specialization. The basic idea is that we start with the basic TLS 1.3 handshake, which is fully general and then remove degrees of freedom, eliding parts of the handshake which are used to express those degrees of freedom.  For example, if we only support one version of TLS, then it is not necessary to have version negotiation and the supported_versions extension can be omitted. </del>  Importantly, this process is performed only for the wire encoding but not for the handshake transcript.  The result is that the transcript One way of thinking of this is as if specialization is a stateful compression layer between the handshake and the record layer: <del> Specializations are defined by a "compression profile" that specifies what features are to be optimized out of the handshake.  In the following subsections, we define the structure of these profiles, and how they are used in compressing and decompressing handshake messages. </del>  <del> 5.1. </del>  <del> A compression profile defining of a specialized version of TLS is defined using a JSON dictionary.  Each axis of specialization is a key in the dictionary.  [[OPEN ISSUE: If we ever want to serialize this, we'll want to use a list instead.]]. </del>  For example, the following specialization describes a protocol with a single fixed version (TLS 1.3) and a single fixed cipher suite ServerHello.cipher_suites, and the supported_versions extensions in the ClientHello and ServerHello would be omitted. <del> cTLS allows specialization along the following axes: The identifier for this profile, to be sent in the "profile_id" field of CTLSPlaintext records.  This value MUST NOT be zero.  If this value is not present, the default "profile_id" is 1. </del>  <del> If present and set to true, the sequence number field is omitted from encrypted record headers. </del>  indicates that both sides agree to the single TLS version specified by the given integer value (772 == 0x0304 for TLS 1.3). "signature_algorithms" extension consisting solely of this group. indicates that the ClientHello.Random and ServerHello.Random <del> values are truncated to the given values.  When the transcript is </del>  reconstructed, the Random is padded to the right with 0s and the anti-downgrade mechanism in RFC8446), Section 4.1.3 is disabled. IMPORTANT: Using short Random values can lead to potential <del> attacks.  When Random values are shorter than 8 bytes, PSK-only modes MUST NOT be used, and each side MUST use fresh DH ephemerals.  The Random length MUST be less than or equal to 32 </del>  bytes.  Predefined ClientHello extensions, see {predefined-extensions} Predefined ServerHello extensions, see {predefined-extensions} indicates that the Finished value is to be truncated to the given length.  When the transcript is reconstructed, the remainder of <del> the Finished value is filled in by the receiving side.  [[OPEN ISSUE: How short should we allow this to be?  TLS 1.3 uses the native hash and TLS 1.2 used 12 bytes.  More analysis is needed to know the minimum safe Finished size.  See RFC8446; Section E.1 for more on this, as well as https://mailarchive.ietf.org/arch/msg/tls/ TugB5ddJu3nYg7chcyeIyUqWSbA.]] </del>  <del> 5.1.1. </del>  To be compatible with the specializations described in this section, <del> a TLS stack needs to provide two key features: </del>  <del> If specialization of extensions is to be used, then the TLS stack MUST order each vector of Extension values in ascending order according to the ExtensionType.  This allows for a deterministic reconstruction of the extension list. </del>  <del> If truncated Random values are to be used, then the TLS stack MUST be configurable to set the remaining bytes of the random values to zero. This ensures that the reconstructed, padded random value matches the original. </del>  <del> If truncated Finished values are to be used, then the TLS stack MUST be configurable so that only the provided bytes of the Finished are verified, or so that the expected remaining values can be computed. </del>  <del> 5.1.2. </del>  Extensions used in the ClientHello, ServerHello, EncryptedExtensions, and CertificateRequest messages can be "predefined" in a compression [[OPEN ISSUE: Are there other extensions that would benefit from special treatment, as opposed to hex values.]] <del> 5.1.3. </del>  Certificates are a major contributor to the size of a TLS handshake. In order to avoid this overhead when the parties to a handshake have byte of the compressed value (key) to have a value other than 0x30, since every X.509 certificate starts with this byte. <del> 6. </del>  <del> The following section provides some example specializations. </del>  <del> TLS 1.3 only: </del>  <del> TLS 1.3 with AES_GCM and X25519 and ALPN h2, short random values, and everything else is ordinary TLS 1.3. </del>  Version 772 corresponds to the hex representation 0x0304, named group "29" (0x001D) represents X25519. [[OPEN ISSUE: Should we have a registry of well-known profiles?]] <del> 7. </del>  WARNING: This document is effectively brand new and has seen no analysis.  The idea here is that cTLS is isomorphic to TLS 1.3, and determine whether we need an extension to indicate that cTLS is in use and with which profile. <del> 8. </del>  This document requests that a code point be allocated from the "TLS ContentType registry.  This value must be in the range 0-31

This update aims to bring the template-based optimization to the center and focuses on the certificiate-based authentication exchange. Included in this version is the idea to omit the random values transmitted by the client and the server by hashes of the ephemeral public keys.

Omitting the fields and handshake messages required for preserving backwards-compatibility with earlier TLS versions.  <ins> More compact encodings. </ins>  <ins> A template-based specialization mechanism that allows pre- populating information at both endpoints without the need for negotiation. </ins> For the common (EC)DHE handshake with pre-established certificates, cTLS achieves an overhead of 45 bytes over the minimum required by 3.2.  <ins> The transmission overhead in TLS 1.3 is largely contributed to by two factors, : - the negotiation of algorithm parameters, and extensions, as well as - the exchange of certificates. </ins>  <ins> TLS 1.3 supports different credential types and modes that are impacted differently by a compression scheme.  For example, TLS supports certificate-based authentication, raw public key-based authentication as well as pre-shared key (PSK)-based authentication. PSK-based authentication can be used with externally configured PSKs or with PSKs established through tickets. </ins>  <ins> The basic idea of template-based specialization is that we start with the basic TLS 1.3 handshake, which is fully general and then remove degrees of freedom, eliding parts of the handshake which are used to express those degrees of freedom.  For example, if we only support one version of TLS, then it is not necessary to have version negotiation and the supported_versions extension can be omitted. </ins> Importantly, this process is performed only for the wire encoding but not for the handshake transcript.  The result is that the transcript One way of thinking of this is as if specialization is a stateful compression layer between the handshake and the record layer:  <ins> By assuming that out-of-band agreements took place already prior to the start of the cTLS protocol exchange, the amount of data exchanged can be radically reduced.  Because different clients may use different compression templates and because multiple compression templates may be available for use in different deployment environments, a client needs to inform the server about the profile it is planning to use.  The profile field in the ClientHello serves this purpose. </ins>  <ins> Although the template-based specialization mechanisms described here are general, we also include specific mechanism for certificate-based exchanges because those are where the most complexity and size reduction can be obtained.  Most of the other exchanges in TLS 1.3 are highly optimized and do not require compression to be used. </ins>  <ins> The compression profile defining the use of algorithms, algorithm parameters, and extensions is specified via a JSON dictionary. </ins> For example, the following specialization describes a protocol with a single fixed version (TLS 1.3) and a single fixed cipher suite ServerHello.cipher_suites, and the supported_versions extensions in the ClientHello and ServerHello would be omitted.  <ins> The following elements are defined: </ins>  <ins> identifies the profile being defined. </ins> indicates that both sides agree to the single TLS version specified by the given integer value (772 == 0x0304 for TLS 1.3). "signature_algorithms" extension consisting solely of this group. indicates that the ClientHello.Random and ServerHello.Random  <ins> values are truncated to the given length.  When the transcript is </ins> reconstructed, the Random is padded to the right with 0s and the anti-downgrade mechanism in RFC8446), Section 4.1.3 is disabled. IMPORTANT: Using short Random values can lead to potential  <ins> attacks.  The Random length MUST be less than or equal to 32 </ins> bytes. <ins> [[Open Issue: Karthik Bhargavan suggested the idea of hashing ephemeral public keys and to use the result (truncated to 32 bytes) as random values.  Such a change would require a security analysis. ]] indicates whether the client has to be prepared to transmit a Certificate and a CertificateVerify message and whether the server has to transmit a CertificateRequest message. indicates in what order extensions appear in respective messages. This allows to omit sending the type.  If there is only a single extension to be transmitted, then the extension length field can also be omitted.  For example, imagine that only the KeyShare extension needs to be sent in the ClientHello as the only extension.  Then, the following structure is compressed down to (assuming the KeyShare group has been pre- agreed) </ins> Predefined ClientHello extensions, see {predefined-extensions} Predefined ServerHello extensions, see {predefined-extensions} indicates that the Finished value is to be truncated to the given length.  When the transcript is reconstructed, the remainder of  <ins> the Finished value is filled in by the receiving side. </ins>  <ins> [[OPEN ISSUE: How short should we allow this to be?  TLS 1.3 uses the native hash and TLS 1.2 used 12 bytes.  More analysis is needed to know the minimum safe Finished size.  See RFC8446; Section E.1 for more on this, as well as https://mailarchive.ietf.org/arch/msg/tls/ TugB5ddJu3nYg7chcyeIyUqWSbA.]] 3.2.1. </ins> To be compatible with the specializations described in this section,  <ins> a TLS stack needs to provide the following features: </ins>  <ins> If specialization of extensions is to be used, then the TLS stack MUST order each vector of Extension values in ascending order according to the ExtensionType.  This allows for a deterministic reconstruction of the extension list. </ins>  <ins> If truncated Random values are to be used, then the TLS stack MUST be configurable to set the remaining bytes of the random values to zero.  This ensures that the reconstructed, padded random value matches the original. </ins>  <ins> If truncated Finished values are to be used, then the TLS stack MUST be configurable so that only the provided bytes of the Finished are verified, or so that the expected remaining values can be computed. </ins>  <ins> 3.2.2. </ins> Extensions used in the ClientHello, ServerHello, EncryptedExtensions, and CertificateRequest messages can be "predefined" in a compression [[OPEN ISSUE: Are there other extensions that would benefit from special treatment, as opposed to hex values.]]  <ins> 3.2.3. </ins> Certificates are a major contributor to the size of a TLS handshake. In order to avoid this overhead when the parties to a handshake have byte of the compressed value (key) to have a value other than 0x30, since every X.509 certificate starts with this byte.  <ins> 3.3. The only cTLS records that are sent in plaintext are handshake records (ClientHello and ServerHello/HRR).  The content type is therefore constant (it is always "handshake"), so we instead set the "content_type" field to a fixed cTLS-specific value to distinguish cTLS plaintext records from encrypted records, TLS/DTLS records, and other protocols using the same 5-tuple. The "profile_id" field allows the client and server to agree on which compression profile should be used for this session (see template- based-specialization).  This field MUST be set to zero if and only if no compression profile is used.  Non-zero values are negotiated out of band between the client and server, as part of the specification of the compression profile. [[OPEN ISSUE: The profile_id is needed in the ClientHello to inform the server what compression profile to use.  For a ServerHello this field is not required.  Should we make this field optional?]] Encrypted records use DTLS 1.3 record framing, comprising a configuration octet followed by optional connection ID, sequence number, and length fields. The presence and size of the connection ID field is negotiated as in DTLS. As with DTLS, the length field MAY be omitted by clearing the L bit, which means that the record consumes the entire rest of the data in the lower level transport.  In this case it is not possible to have multiple DTLSCiphertext format records without length fields in the same datagram.  In stream-oriented transports (e.g., TCP), the length field MUST be present.  For use over other transports length information may be inferred from the underlying layer. </ins>  <ins> Normal DTLS does not provide a mechanism for suppressing the sequence number field entirely.  In cases where a sequence number is not required (e.g., when a reliable transport is in use), a cTLS implementation may suppress it by setting the "suppressSequenceNumber" flag in the compression profile being used (see specifying-a-specialization).  When this flag is enabled, the S bit in the configuration octet MUST be cleared. 3.4. The cTLS handshake framing is same as the TLS 1.3 handshake framing, except for two changes: The length field is omitted. The HelloRetryRequest message is a true handshake message instead of a specialization of ServerHello. 4. In general, we retain the basic structure of each individual TLS handshake message.  However, the following handshake messages have been modified for space reduction and cleaned up to remove pre-TLS 1.3 baggage. 4.1. The cTLS ClientHello is defined as follows. 4.2. We redefine ServerHello in the following way. </ins>  <ins> 4.3. The HelloRetryRequest has the following format. The HelloRetryRequest is the same as the ServerHello above but without the unnecessary sentinel Random value. 5. </ins>  <ins> This section provides some example specializations. For this example we use TLS 1.3 only with AES_GCM, X25519, ALPN h2, short random values, and everything else is ordinary TLS 1.3. </ins> Version 772 corresponds to the hex representation 0x0304, named group "29" (0x001D) represents X25519. [[OPEN ISSUE: Should we have a registry of well-known profiles?]]  <ins> 6. </ins> WARNING: This document is effectively brand new and has seen no analysis.  The idea here is that cTLS is isomorphic to TLS 1.3, and determine whether we need an extension to indicate that cTLS is in use and with which profile.  <ins> 7. </ins> This document requests that a code point be allocated from the "TLS ContentType registry.  This value must be in the range 0-31


cTLS plaintext records from encrypted records, TLS/DTLS records, and other protocols using the same 5-tuple. <del> The "profile_id" field allows the client and server to agree on which compression profile should be used for this session (see template- based-specialization).  This field MUST be set to zero if and only if no compression profile is used.  Non-zero values are negotiated out of band between the client and server, as part of the specification of the compression profile. </del> [[OPEN ISSUE: The profile_id is needed in the ClientHello to inform the server what compression profile to use.  For a ServerHello this field is not required.  Should we make this field optional?]] The cTLS ClientHello is defined as follows.  3.2. We redefine ServerHello in the following way.

For cTLS/TCP, the CTLSCiphertext headers could easily all be the same size (1 byte type/flags, 2 byte length). For cTLS/UDP, the header sizes could be made more predictable. Suggestions: Always suppress sequence numbers and disallow connection IDs in cTLS/TCP, and after the first record in a datagram in cTLS/UDP. Move into the cTLS ClientHello struct.
Moving the profile_id into the ClientHello sounds good to me. Here is the PR: URL
Suppressing sequence numbers in cTLS/TCP sounds good to me as well. Here is the PR: URL
NAME closing as I think we addressed.

cTLS plaintext records from encrypted records, TLS/DTLS records, and other protocols using the same 5-tuple.  [[OPEN ISSUE: The profile_id is needed in the ClientHello to inform the server what compression profile to use.  For a ServerHello this field is not required.  Should we make this field optional?]] The cTLS ClientHello is defined as follows. <ins> The client uses the "profile_id" field to inform the server about the compression profile being used (see template-based-specialization). This field MUST be set to a zero-length value and only if no compression profile is used.  Non zero-length values are agreed out of band between the client and server, as part of the specification of the compression profile. </ins> 3.2. We redefine ServerHello in the following way.


2.2. The only cTLS records that are sent in plaintext are handshake <del> records (ClientHello and ServerHello/HRR).  The content type is therefore constant (it is always "handshake"), so we instead set the "content_type" field to a fixed cTLS-specific value to distinguish cTLS plaintext records from encrypted records, TLS/DTLS records, and other protocols using the same 5-tuple. </del>  [[OPEN ISSUE: The profile_id is needed in the ClientHello to inform the server what compression profile to use.  For a ServerHello this

NAME NAME
I wonder whether this works. Here is the solution I came up with URL
yeah that seems more complicated. Is there a reason why this won't work?
IMHO we somehow have to distinguish between three types of message (via the content type): plaintext handshake messages (for the ClientHello, ServerHello/HRR) encrypted cTLS messages, and plaintext alert messages Do I see this wrong?
Agreed. But that's plaintext handshake messages (for the ClientHello, ServerHello/HRR) --> ctls_handshake encrypted cTLS messages, and --> The ordinary DTLS rules plaintext alert messages --> tls alert.
I guess I was thinking about this in a too complicated way. Do you want to use the regular TLS Alert message format? (Fine for me.)
That is my proposal.
I think the logical way to do this would be to register in the . Otherwise, you're setting up a potential collision as more values are added to that registry.
Yes, I agree with that. I'm just talking about the technical approach
OK, then I think this PR might as well add the IANA instruction for that.
I made a separate IANA PR URL
It’s currently not clear how plaintext Alerts are represented. Suggestion: contenttype = ctlsalert
I created a PR to address this issue: URL
NAME why can't they just be the usual content type? What am I missing.
The current text says records (ClientHello and ServerHello/HRR). The content type is therefore constant (it is always handshake), so we instead set the contenttype field to a fixed cTLS-specific value to distinguish cTLS plaintext records from encrypted records, TLS/DTLS records, and other protocols using the same 5-tuple. I'm proposing that: The first sentence (or maybe the second) is not right. We need another for plaintext alerts. I'm not sure what you mean by "the usual content type". cTLS currently does not use TLS's ContentType registry. Instead, the current draft defines a new value to identify these records. If you're proposing to switch cTLS to use the TLS ContentTypes instead ... that seems fine to me. However, the divergence does seem to be deliberate, as explained in the text.
I think this is just an error on my part: You obviously need to be able to send plaintext alerts. The requirement is to be able to demux, but if cTLS alerts are the same, then no need For that reason, I propose to just use TLS's alert type.

2.2. The only cTLS records that are sent in plaintext are handshake  <ins> records (ClientHello and ServerHello/HRR) and alerts. cTLS alerts are the same as TLS alerts and use the same content types.  For handshake records, we set the "content_type" field to a fixed cTLS-specific value to distinguish cTLS plaintext records from encrypted records, TLS/DTLS records, and other protocols using the same 5-tuple. </ins> [[OPEN ISSUE: The profile_id is needed in the ClientHello to inform the server what compression profile to use.  For a ServerHello this


information may be inferred from the underlying layer. Normal DTLS does not provide a mechanism for suppressing the sequence <del> number field entirely.  In cases where a sequence number is not required (e.g., when a reliable transport is in use), a cTLS implementation may suppress it by setting the "suppressSequenceNumber" flag in the compression profile being used (see template-based-specialization).  When this flag is enabled, the S bit in the configuration octet MUST be cleared. </del>  2.3.

I'm slightly concerned about the fact that we have two different wire behaviors with the same transcript, but because the integrity tag includes the header, I think it's OK. NAME would welcome your thoughts.
I think I have addressed this aspect already in PR URL
Correct. I thought Ben's suggestion to tie it to transport made more sense, but I'm not hard over on it.
Fine for me too.
S = "sequence number"

information may be inferred from the underlying layer. Normal DTLS does not provide a mechanism for suppressing the sequence  <ins> number field entirely.  When a reliable, ordered transport (e.g., TCP) is in use, the S bit in the configuration octet MUST be cleared and the sequence number MUST be omitted.  When an unreliable transport is in use, the S bit has its usual meaning and the sequence number MUST be included. </ins> 2.3.


The following elements are defined: <del> identifies the profile being defined. </del>  indicates that both sides agree to the single TLS version specified by the given integer value (772 == 0x0304 for TLS 1.3). value to distinguish cTLS plaintext records from encrypted records, TLS/DTLS records, and other protocols using the same 5-tuple. <del> OPEN ISSUE: The profile_id is needed in the ClientHello to inform the server what compression profile to use.  For a ServerHello this field is not required.  Should we make this field optional? </del> Encrypted records use DTLS 1.3 RFC9147 record framing, comprising a configuration octet followed by optional connection ID, sequence number, and length fields.  The encryption process and additional The cTLS ClientHello is defined as follows. <del> The client uses the "profile_id" field to inform the server about the compression profile being used (see template-based-specialization). This field MUST be set to a zero-length value and only if no compression profile is used.  Non zero-length values are agreed out of band between the client and server, as part of the specification of the compression profile. </del>  3.2. Version 772 corresponds to the hex representation 0x0304, named group "29" (0x001D) represents X25519. <del> OPEN ISSUE: Should we have a registry of well-known profiles? </del> 5. WARNING: This document is effectively brand new and has seen no Template Keys", on the Transport Layer Security (TLS) Parameters page, with a "Specification Required" registration policy and the following initial contents:

This change also reserves short profile IDs for "well-known" use. Fixes URL
Can we please try to resolve this question soon to unblock implementation?
NAME NAME Any objection to merging this?
The template says it's an integer (in what range?), but the ClientHello says it's 0-255 bytes. I think it's probably easiest to say it's a string of 0-255 bytes, with the empty string reserved to mean "no template". I guess if you want to get fancy you could reserve all 1-4 byte strings.
Agreed. If you want to send a PR, I wouldn't object.
This LGTM.

The following elements are defined:  <ins> identifies the profile being defined (default: "").  If present, this key MUST contain a hex-encoded sequence of 0-255 bytes (the "profile ID").  IDs whose decoded length is 4 bytes or less are reserved (see reserved-profiles).  When a reserved value is used (including the default value), other keys MUST NOT appear in the template, and a client MUST NOT accept the template unless it recognizes the ID. </ins> indicates that both sides agree to the single TLS version specified by the given integer value (772 == 0x0304 for TLS 1.3). value to distinguish cTLS plaintext records from encrypted records, TLS/DTLS records, and other protocols using the same 5-tuple.  Encrypted records use DTLS 1.3 RFC9147 record framing, comprising a configuration octet followed by optional connection ID, sequence number, and length fields.  The encryption process and additional The cTLS ClientHello is defined as follows.  <ins> The "profile_id" field MUST identify the profile that is in use.  A zero-length ID corresponds to the cTLS default protocol. </ins> 3.2. Version 772 corresponds to the hex representation 0x0304, named group "29" (0x001D) represents X25519.  5. WARNING: This document is effectively brand new and has seen no Template Keys", on the Transport Layer Security (TLS) Parameters page, with a "Specification Required" registration policy and the following initial contents: <ins> 6.3. This document requests that IANA open a new registry entitled "Well- known cTLS Profile IDs", on the Transport Layer Security (TLS) Parameters page, with the following columns: ID value: A sequence of 1-4 octets. Template: A JSON object. Note: An explanation or reference. The ID values of length 1 are subject to a "Standards Action" registry policy.  Values of length 2 are subject to an "RFC Required" policy.  Values of length 3 and 4 are subject to a "First Come First Served" policy.  Values longer than 4 octets are not subject to registration and MUST NOT appear in this registry. The initial registry contents are: </ins>


This is equivalent to a placing a literal "signature_algorithms" extension consisting solely of this group in every extensions field <del> where it is permitted to appear. </del>  In JSON, the signature algorithm is listed by the code point name in RFC8446, Section 4.2.3. (e.g., ecdsa_secp256r1_sha256). ascending order by "ExtensionType", and a single "ExtensionType" MUST NOT appear in both lists.  If the "version", "dh_group", or "signature_algorithm" element appears in the template, the <del> corresponding "ExtensionType" MUST NOT appear here. </del>  OPEN ISSUE: Are there other extensions that would benefit from special treatment, as opposed to hex values. Static vectors (see static-vectors): "Extension.extension_data" for any extension in <del> "expected_extensions" whose value has fixed length.  This applies only to the corresponding message. </del>  The "extensions" field of the corresponding message, if "allow_additional" is false. Value: "uint8", with 0 indicating "false" and 1 indicating "true". If true, handshake messages MUST be conveyed inside a "Handshake" <del> (RFC8446, Section 4) struct on stream transports, or a "DTLSHandshake" (RFC9147, Section 5.2) struct on datagram transports, and MAY be broken into multiple records as in TLS and DTLS. Otherwise, each handshake message is conveyed in a "CTLSHandshake" or </del>  "CTLSDatagramHandshake" struct (ctlshandshake), which MUST be the payload of a single record. byte of the compressed value (key) to have a value other than 0x30, since every X.509 certificate starts with this byte.  2.1.2. Some cTLS template elements imply that certain vectors (as defined in their definition.  When encoding a "static vector", its length prefix is omitted.  For example, suppose that the cTLS template is: Then, the following structure: undesirable.  Operators can avoid these concerns by setting "template.handshakeFraming = true".  2.3.2. TLS and DTLS start the handshake with an empty transcript.  cTLS is represented in TLS "Handshake" messages, as in DTLS 1.3 (RFC9147, Section 5.2), regardless of "template.handshake_framing". <del> To ensure that all parties agree about what protocol is in use, the Cryptographic Label Prefix used for the handshake SHALL be "Sctls " for Stream cTLS and "Dctls " for Datagram cTLS.  (This is similar to the prefix substitution in Section 5.9 of RFC9147). </del>  2.3.3. The HelloRetryRequest is the same as the ServerHello above but without the unnecessary sentinel Random value. <del> OPEN ISSUE: Does "server_hello_extensions" apply to "HelloRetryRequest"? </del>  4.

NAME Ping. This is the last thing blocking a refreshed cTLS draft.
Just missed this. LGTM

This is equivalent to a placing a literal "signature_algorithms" extension consisting solely of this group in every extensions field  <ins> where it is permitted to appear.  When this element is included, "CertificateVerify.algorithm" is omitted. Static vectors (see static-vectors): * "CertificateVerify.signature", if the "SignatureScheme" output is self-delimiting. </ins> In JSON, the signature algorithm is listed by the code point name in RFC8446, Section 4.2.3. (e.g., ecdsa_secp256r1_sha256). ascending order by "ExtensionType", and a single "ExtensionType" MUST NOT appear in both lists.  If the "version", "dh_group", or "signature_algorithm" element appears in the template, the  <ins> corresponding "ExtensionType" MUST NOT appear here.  The "pre_shared_key" "ExtensionType" MUST NOT appear in either list. </ins> OPEN ISSUE: Are there other extensions that would benefit from special treatment, as opposed to hex values. Static vectors (see static-vectors): "Extension.extension_data" for any extension in  <ins> "expected_extensions" whose value is self-delimiting (e.g., fixed length).  This applies only to the corresponding message. </ins> The "extensions" field of the corresponding message, if "allow_additional" is false. Value: "uint8", with 0 indicating "false" and 1 indicating "true". If true, handshake messages MUST be conveyed inside a "Handshake"  <ins> (RFC8446, Section 4) struct on reliable, ordered transports, or a "DTLSHandshake" (RFC9147, Section 5.2) struct otherwise, and MAY be broken into multiple records as in TLS and DTLS.  If false, each handshake message is conveyed in a "CTLSHandshake" or </ins> "CTLSDatagramHandshake" struct (ctlshandshake), which MUST be the payload of a single record. byte of the compressed value (key) to have a value other than 0x30, since every X.509 certificate starts with this byte. <ins> This element can be used to compress both client and server certificates.  However, in most deployments where client certificates are used, it would be inefficient to encode all client certificates into a single profile.  Instead, deployments can define a unique profile for each client, distinguished by the profile ID.  Note that the profile ID is sent in cleartext, so this strategy has significant privacy implications. </ins> 2.1.2. Some cTLS template elements imply that certain vectors (as defined in their definition.  When encoding a "static vector", its length prefix is omitted. <ins> In addition to the static vectors implied by various template elements, "Extension.extension_data" is always static in cTLS if the extension is any type listed in Section 4.2 of RFC8446 except "padding".  cTLS implementations MUST be able to parse any of these extensions that the other party is permitted to send, but no other support is required. </ins> For example, suppose that the cTLS template is: Then, the following structure: undesirable.  Operators can avoid these concerns by setting "template.handshakeFraming = true". <ins> On unreliable transports, the DTLS 1.3 ACK system is used. </ins> 2.3.2. TLS and DTLS start the handshake with an empty transcript.  cTLS is represented in TLS "Handshake" messages, as in DTLS 1.3 (RFC9147, Section 5.2), regardless of "template.handshake_framing".  <ins> To ensure that all parties agree about what protocol is in use, and whether records are subject to loss, the Cryptographic Label Prefix used for the handshake SHALL be "Sctls " (for "Stream cTLS") if "Handshake" or "CTLSHandshake" transport was used, and "Dctls " (for "Datagram cTLS") otherwise.  (This is similar to the prefix substitution in Section 5.9 of RFC9147). </ins> 2.3.3. The HelloRetryRequest is the same as the ServerHello above but without the unnecessary sentinel Random value.  <ins> OPEN ISSUE: Should we define a "hello_retry_request_extensions" template element?  Or is this too far off the happy path to be worth optimizing? </ins> 4.


Value: a single "SignatureScheme" (RFC8446, Section 4.2.3) to use for authentication. <del> This is equivalent to a literal "signature_algorithms" extension consisting solely of this group. </del>  In JSON, the signature algorithm is listed by the code point name in RFC8446, Section 4.2.3. (e.g., ecdsa_secp256r1_sha256). If set to true, this element indicates that the client must authenticate with a certificate by sending Certificate and a <del> CertificateVerify message.  The server MUST omit the CertificateRequest message, as its contents are redundant. OPEN ISSUE: We don't actually say that you can omit empty messages, so we need to add that somewhere. </del>  In JSON, this value is represented as "true" or "false".

Empty messages must now be included. If the body is known to be empty from the template, this costs one byte. This change also adjusts the explanation of and , and corrects the example.
NAME points out that EndOfEarlyData is an empty message that is semantically meaningful and cannot be predicted by the recipient.

Value: a single "SignatureScheme" (RFC8446, Section 4.2.3) to use for authentication.  <ins> This is equivalent to a placing a literal "signature_algorithms" extension consisting solely of this group in every extensions field where it is permitted to appear. </ins> In JSON, the signature algorithm is listed by the code point name in RFC8446, Section 4.2.3. (e.g., ecdsa_secp256r1_sha256). If set to true, this element indicates that the client must authenticate with a certificate by sending Certificate and a  <ins> CertificateVerify message.  If the CertificateRequest message does not add information not already conveyed in the template, the server SHOULD omit it. </ins> In JSON, this value is represented as "true" or "false".


needed to know the minimum safe Finished size.  See RFC8446, Section E.1 for more on this, as well as https://mailarchive.ietf.org/arch/msg/tls/ <del> TugB5ddJu3nYg7chcyeIyUqWSbA. </del>  In JSON, this length is represented as an integer. source of identity misbinding.  This is, however, entirely separable from the rest of the specification. <del> Transcript expansion also needs some analysis and we need to determine whether we need an extension to indicate that cTLS is in use and with which profile. </del>  6.

LGTM.
Is it a design goal of this draft to support secure connection, even if the compression template is distributed over an untrusted channel? I believe it is a design goal, and I think it's probably achieved by the current draft (except for questions around the and keys), but it's not stated explicitly in the draft.

needed to know the minimum safe Finished size.  See RFC8446, Section E.1 for more on this, as well as https://mailarchive.ietf.org/arch/msg/tls/  <ins> TugB5ddJu3nYg7chcyeIyUqWSbA.  The minimum safe size may vary depending on whether the template was learned via a trusted channel. </ins> In JSON, this length is represented as an integer. source of identity misbinding.  This is, however, entirely separable from the rest of the specification.  <ins> Once the handshake has completed, this specification is intended to provide a fully secured connection even if the client initially learned the template through an untrusted channel.  However, this security relies on the use of a cryptographically strong Finished message.  If the Finished message has not yet been received, or the transcript hash has been truncated by use of a small "finished_size" template element value, an attacker could be using a forged template to impersonate the other party.  This should not impact any ordinary use of TLS, including Early Data (which is secured by the previously completed handshake). </ins> 6.


"extensions" field.  The types of these extensions are omitted when serializing the "extensions" field of the corresponding message.  The "allow_additional" field MUST be 0 (false) or 1 (true), indicating whether additional extensions are allowed here. Static vectors (see static-vectors): <del> "Extension.extension_data" for any extension in "expected_extensions" whose value is self-delimiting (e.g., fixed length).  This applies only to the corresponding message. </del>  The "extensions" field of the corresponding message, if "allow_additional" is false. "expectedExtensions": an array of "ExtensionType" names.  "allowAdditional": "true" or "false". If "predefinedExtensions" or "expectedExtensions" is empty, it MAY be their definition.  When encoding a "static vector", its length prefix is omitted. <del> In addition to the static vectors implied by various template elements, "Extension.extension_data" is always static in cTLS if the extension is any type listed in Section 4.2 of RFC8446 except "padding".  cTLS implementations MUST be able to parse any of these extensions that the other party is permitted to send, but no other support is required. </del> For example, suppose that the cTLS template is: Then, the following structure:

In the case a cTLS handshake uses a self-delimiting extension (in additional extensions) that is not present in section 4.2 of RFC 8446, its length is always serialized. With this PR, the type of this extension can be stored in and its length can be omitted.

"extensions" field.  The types of these extensions are omitted when serializing the "extensions" field of the corresponding message. <ins> The "self_delimiting_extensions" field indicates extensions whose data is self-delimiting.  The cTLS implementation MUST be able to parse all these extensions, and all extensions listed in Section 4.2 of RFC8446. </ins> The "allow_additional" field MUST be 0 (false) or 1 (true), indicating whether additional extensions are allowed here. Static vectors (see static-vectors):  <ins> "Extension.extension_data" for any extension whose type is in "self_delimiting_extensions", or is listed in Section 4.2 of RFC8446 except "padding".  This applies only to the corresponding message. </ins> The "extensions" field of the corresponding message, if "allow_additional" is false. "expectedExtensions": an array of "ExtensionType" names. <ins> "selfDelimitingExtensions": an array of "ExtensionType" names. </ins> "allowAdditional": "true" or "false". If "predefinedExtensions" or "expectedExtensions" is empty, it MAY be their definition.  When encoding a "static vector", its length prefix is omitted.  For example, suppose that the cTLS template is: Then, the following structure:


again. For the common (EC)DHE handshake with pre-established certificates, <del> Stream cTLS achieves an overhead of 45 bytes over the minimum </del>  required by the cryptovariables.  For a PSK handshake, the overhead <del> is 21 bytes.  Annotated handshake transcripts for these cases can be found in transcripts. </del>  <del> TODO: Update these values. </del>  cTLS supports the functionality of TLS and DTLS 1.3, and is forward- compatible to future versions of TLS and DTLS.  cTLS itself is In JSON, this value is represented as "true" or "false".  2.1.1.8. Value: a single "CTLSExtensionTemplate" struct:

The example did not account for the record layer, and seemed to overlook some other details as well.
CTLSClientPlaintext and CTLSServerPlaintext seem to be included in the example exchange but CTLSCiphertext seems to be missing. My understanding is that ServerFlight and ClientFlight are sent over CTLSCiphertext which would add a mimimum of 2 * 11 bytes. It is not explained why the signature is 64 bytes. In TLS 1.3, my understanding is that ecdsasecp256r1sha256 is variable length 12-76 bytes with an average length close to 75 bytes. Changing ecdsasecp256r1sha256 to ed25519 (or ecdsasecp256r1sha256_compact) would make it 64 bytes. The introduction promises an example PSK handshake, but that is missing.
NAME Please review , which attempts to fix the example as you've described.

again. For the common (EC)DHE handshake with pre-established certificates,  <ins> Stream cTLS achieves an overhead of 53 bytes over the minimum </ins> required by the cryptovariables.  For a PSK handshake, the overhead  <ins> is 21 bytes.  An annotated handshake transcript can be found in transcripts. </ins>  <ins> TODO: Make a PSK transcript and check the overhead. </ins> cTLS supports the functionality of TLS and DTLS 1.3, and is forward- compatible to future versions of TLS and DTLS.  cTLS itself is In JSON, this value is represented as "true" or "false". <ins> TODO: It seems like there was an intent to elide the "Certificate.certificate_request_context" field, but this is not stated explicitly anywhere. </ins> 2.1.1.8. Value: a single "CTLSExtensionTemplate" struct:


Description: ctls_template <del> DTLS-OK: ??? Not clear what to put here. </del>  Reference: (This document)

For the ctls_template message type it is OK to set the DTLS-OK field to "Y". The entire message is not applicable to regular TLS/DTLS and hence there is no harm whatever value is in the IANA registry.
I still think it's unclear what this means, but I agree that it doesn't matter much and "Y" is ... not wrong.

Description: ctls_template  <ins> DTLS-OK: Y </ins> Reference: (This document)


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

The TLS server name (and thus SNI) is the HTTP origin name, however it is not necessarily the same as the host name of the TLS server. HTTP Alternative Services is the obvious way that happens - but Alt-Svc never changes the origin name. When fetching the ESNI key, do we fetch it from URL or URL I think origin-name makes more sense - keeping everything that is an expiicit protocol element in terms of origin eliminates confusion. the counter argument is that you can use alt-svc to do fine grained fanout to providers with different keys (using cdn specific names for them in alt-svc) in a way that is harder to do through the origin name dns controls.
My view is that the base name that is used to lookup the ESNI value (i.e. the name excluding the prefix) must be the same as the hostname that is carried by the ESNI extension. ESNI is a replacement for SNI, and I think that the design should allow the TLS stack to obtain the ESNI key by itself (i.e. by querying ).
PS. To give an example, golang's TLS API to establish a new connection looks like below. As is obvious from the example, name resolution happens inside the TLS stack. I'd assume that we would want to handle ESNI record lookup within the stack as well. Therefore, the name used to lookup the ESNI record cannot be the origin name, which is specific to the behavior defined in HTTP (alt-svc), which operates above the TLS stack. It needs to be the hostname that is used for the name resolution (or a name that is derived from that). My understanding is that TLS stacks on other programming language often has the same abstraction; i.e. handle name resolution inside in the TLS connection establishment call (or the TCP connection establishment call).
This matches my opinion, and also what firefox nightly is currently doing.. but I'm confused by your next comment afaict the SNI information actually comes from tls.Config (Falling back to URL only if not provided).. this is no doubt also the information used to authenticate the cert.. URL The TLS stack is going to need both pieces of information. So which should it use? no - the origin name is the SNI name even with alt-svc. It might not be the host name. If a stack can't support alt-svc, then this isn't a problem - the name is the same everywhere :) If a stack does support alt-svc then it has 2 names to deal with.. one is the name used for SNI and auth, and the other is the one used for TCP. (or perhaps it doesn't do TCP on its own). All this issue is trying to do is decide whether the name we lookup for ENSNI/TXT is the same as the name we lookup for A/AAAA.. I'm going to say that they don't necessarily have to match. (i.e. your A could be for the alt-svc but your txt/esni is always going to be for the origin - which is the same thing you put in your CH. I'll make a pr.

normal A or AAAA queries, and SHOULD block TLS handshakes until they complete, perhaps by timing out. <ins> In cases where the domain of the A or AAAA records being resolved do not match the SNI Server Name, such as when RFC7838 is being used, the SNI domain should be used for querying the ESNI TXT record. </ins> 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


This design is not forward secret because the server's ESNI key is static.  However, the window of exposure is bound by the key <del> lifetime.  It is RECOMMEMDED that servers rotate keys frequently. </del>  7.2.6.

There were two minor typos. This PR fixes them. While I was at it, I ran over it, most seemed fine.

This design is not forward secret because the server's ESNI key is static.  However, the window of exposure is bound by the key  <ins> lifetime.  It is RECOMMENDED that servers rotate keys frequently. </ins> 7.2.6.


The label associated with the SNI encryption key. <del> A cryptographically random 128-bit nonce </del> The cipher suite used to encrypt the SNI. The original ServerNameList from the "server_name" extension, The SNI encryption key is computed from the DH shared secret Z as follows: <del> The EncryptedSNI.encrypted_sni value is then computed by: </del>  <del> [[OPEN ISSUE: This is a strawman construction.  We do want a nonce to avoid situations where the server somehow reuses a key, but exactly how we mix it in is TBD.  Maybe in both places?]] </del>  This value is placed in an "encrypted_server_name" extension.

You are mixing URL in. That would seem to be sufficient. From that you can generate the AEAD nonce and key as though this were record protection. We don't need another reason for people to complain about exposing the random number generator state to the network.
If servers choose to use the same ESNI and semi-static key, not generating and including a nonce could lead to nonce-reuse for 0-RTT data. We talked about just prohibiting this sort of key sharing for servers, though it's good to have a client-side defense against it.

The label associated with the SNI encryption key.  The cipher suite used to encrypt the SNI. The original ServerNameList from the "server_name" extension, The SNI encryption key is computed from the DH shared secret Z as follows:  <ins> 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 ESNIKeyShare for other purposes within the same message (e.g., encrypting other values).  Those usages MUST have their own HKDF labels to avoid reuse. </ins>  <ins> [[OPEN ISSUE: If in future you were to reuse these keys for 0-RTT priming, then you would have to worry about potentially expanding twice of Z_extracted.  We should think about how to harmonize these.]] </ins> This value is placed in an "encrypted_server_name" extension.


3.2. <del> The protocol designed in this document is quite straightforward. First, the provider publishes a public key which is used for SNI encryption for all the domains for which it serves directly or indirectly (via Split Mode).  This document defines a publication </del>  mechanism using DNS, but other mechanisms are also possible.  In particular, if some of the clients of a private server are applications rather than Web browsers, those applications might have <del> the public key preconfigured. </del>  When a client wants to form a TLS connection to any of the domains <del> served by an ESNI-supporting provider, it replaces the "server_name" extension in the ClientHello with an "encrypted_server_name" 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 the backend server (in Split Mode). </del>  4. structure, defined below. The version of the structure.  For this specification, that value <del> SHALL be 0xff01.  Clients MUST ignore any ESNIKeys structure with </del>  a version they do not understand.  [[NOTE: This means that the RFC will presumably have a nonzero value.]] the ESNIKeys structure.  For the purpose of computing the checksum, the value of the "checksum" field MUST be set to zero.  The list of keys which can be used by the client to encrypt the SNI.  Every key being listed MUST belong to a different group. For servers (in EncryptedExtensions), this extension contains the following structure:  The contents of ClientESNIInner.nonce.  (See client-behavior.)  5.1.  In order to send an encrypted SNI, the client MUST first select one of the server ESNIKeyShareEntry values and generate an (EC)DHE share in the matching group.  This share will then be sent to the server in ESNI TXT query).  The client MUST NOT send encrypted SNI using groups or cipher suites not advertised by the server.  Let Z be the DH shared secret derived from a key share in ESNIKeys and the corresponding client share in ClientEncryptedSNI.key_share. The SNI encryption key is computed from Z as follows: This value is placed in an "encrypted_server_name" extension. <del> The client MAY either omit the "server_name" extension or provide an innocuous dummy one (this is required for technical conformance with RFC7540; Section 9.2.) </del>  <del> If the server does not negotiate TLS 1.3 or above, then the client MUST abort the connection with an "unsupported_version" alert.  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 ClientESNIInner.nonce matches the value of the "encrypted_server_name" extension provided by the server, and otherwise abort the connection with an "illegal_parameter" alert. </del>  5.2. Upon receiving an "encrypted_server_name" extension, the client- <del> facing server MUST first perform the following checks: If it is unable to negotiate TLS 1.3 or greater, it MUST abort the connection with a "handshake_failure" alert. If the ClientEncryptedSNI.record_digest value does not match the cryptographic hash of any known ESNIKeys structure, it MUST abort the connection with an "illegal_parameter" alert.  This is necessary to prevent downgrade attacks.  [[OPEN ISSUE: We looked at ignoring the extension but concluded this was better.]] </del>  If the ClientEncryptedSNI.key_share group does not match one in the ESNIKeys.keys, it MUST abort the connection with an such that its length equals the size of the largest possible Certificate (message) covered by the same ESNI key.  Moreover, the server MUST include the "encrypted_server_name" extension in <del> EncryptedExtensions, and the value of this extension MUST match PaddedServerNameList.nonce. </del>  5.4. to the backend server.  Thus, backend servers function the same as servers operating in Shared Mode.  6. <del> In general, this mechanism is designed only to be used with servers which have opted in, thus minimizing compatibility issues.  However, there are two scenarios where that does not apply, as detailed below. </del>  6.1. <del> 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 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. </del>  6.2. A more serious problem is MITM proxies which do not support this extension.  RFC8446; Section 9.3 requires that such proxies remove <del> any extensions they do not understand.  This will have one of two results when connecting to the client-facing server: The handshake will fail if the client-facing server requires SNI. The handshake will succeed with the client-facing server's default certificate. A Web client client can securely detect case (2) because it will result in a connection which has an invalid identity (most likely) but which is signed by a certificate which does not chain to a publicly known trust anchor.  The client can detect this case and disable ESNI while in that network configuration. In order to enable this mechanism, client-facing servers SHOULD NOT require SNI, but rather respond with some default certificate. A non-conformant MITM proxy will forward the ESNI extension, substituting its own KeyShare value, with the result that the client- facing server will not be able to decrypt the SNI.  This causes a hard failure.  Detecting this case is difficult, but clients might opt to attempt captive portal detection to see if they are in the presence of a MITM proxy, and if so disable ESNI.  Hopefully, the TLS 1.3 deployment experience has cleaned out most such proxies. </del>  7. operating in Split Mode is not possible.  See cleartext-dns for more details regarding cleartext DNS.  7.2.8. This design has no impact on application layer protocol negotiation. 8.1. <del> IANA is requested to Create an entry, encrypted_server_name(0xffce), </del>  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". 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

(WG email coming shortly.) ESNI has different compatibility characteristics than most TLS extensions. It involves client state, and encrypting the SNI makes the ClientHello unusable for servers that do not have the key or do not implement ESNI. This breaks common rollout and rollback strategies and places strong requirements on DNS/server consistency. This makes deploying ESNI risky for servers. This change aims to reduce these deployment risks. ESNI is much more useful if widely deployed. Moreover, if ESNI is unreliable due to complex requirements on the server, clients will be disincentivized to implement it. The DNS/server consistency requirements also disincentivize rotating keys (section 7.2.5). This change introduces the notion of a "public name" which allows the client to receive authenticated signals from the server that the ESNIKeys are out of sync, so clients can safely repair key mismatches. It also aligns with existing server behavior before ESNI, so partial rollouts and rollbacks can also be handled. The fix for the latter also handles MITM scenarios, replacing the existing client recommendation: The public name notion fixes a bug in that text: an attacker could disable ESNI on all servers, given the existence of any such server.
And, as I write this, I realize I forgot to deal with Stephen's comment on the thread about the middlebox stuff! It's quite late here, so I will reword that tomorrow morning.
Currently, the connect fails when a mismatch occurs. An alternative strategy might be to return a new ESNI key that is then used for a subsequent connection.
How to understand the reliability of the public key from server?
I see that I missed something about disabling. I think that this could be a lot more explicit about the three primary cases that I talk about in the comments below.Looks mostly good, but added some nits and clarification questions.

3.2.  <ins> First, the provider publishes a public key and metadata which is used for SNI encryption for all the domains for which it serves directly or indirectly (via Split Mode).  This document defines a publication </ins> mechanism using DNS, but other mechanisms are also possible.  In particular, if some of the clients of a private server are applications rather than Web browsers, those applications might have  <ins> the public key and metadata preconfigured. </ins> When a client wants to form a TLS connection to any of the domains  <ins> served by an ESNI-supporting provider, it sends an "encrypted_server_name" 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 the backend server (in Split Mode). </ins> 4. structure, defined below. The version of the structure.  For this specification, that value  <ins> SHALL be 0xff02.  Clients MUST ignore any ESNIKeys structure with </ins> a version they do not understand.  [[NOTE: This means that the RFC will presumably have a nonzero value.]] the ESNIKeys structure.  For the purpose of computing the checksum, the value of the "checksum" field MUST be set to zero. <ins> The non-empty name of the entity trusted to update these encryption keys.  This is used to repair misconfigurations, as described in handle-server-response. </ins> The list of keys which can be used by the client to encrypt the SNI.  Every key being listed MUST belong to a different group. For servers (in EncryptedExtensions), this extension contains the following structure: <ins> Indicates whether the server processed the client ESNI extension. (See handle-server-response and server-behavior.} </ins> The contents of ClientESNIInner.nonce.  (See client-behavior.) <ins> One or more ESNIKeys structures containing the keys that the client should use on subsequent connections to encrypt the ClientESNIInner structure. This protocol also defines the "esni_required" alert, which is sent by the client when it offered an "encrypted_server_name" extension which was not accepted by the server. Finally, requirements in client-behavior and server-behavior require implementations to track, alongside each PSK established by a previous connection, whether the connection negotiated this extension with the "esni_accept" response type.  If so, this is referred to as an "ESNI PSK".  Otherwise, it is a "non-ESNI PSK".  This may be implemented by adding a new field to client and server session states. </ins> 5.1. <ins> 5.1.1. </ins> In order to send an encrypted SNI, the client MUST first select one of the server ESNIKeyShareEntry values and generate an (EC)DHE share in the matching group.  This share will then be sent to the server in ESNI TXT query).  The client MUST NOT send encrypted SNI using groups or cipher suites not advertised by the server. <ins> When offering an encrypted SNI, the client MUST NOT offer to resume any non-ESNI PSKs.  It additionally MUST NOT offer to resume any sessions for TLS 1.2 or below. </ins> Let Z be the DH shared secret derived from a key share in ESNIKeys and the corresponding client share in ClientEncryptedSNI.key_share. The SNI encryption key is computed from Z as follows: This value is placed in an "encrypted_server_name" extension.  <ins> The client MUST place the value of ESNIKeys.public_name in the "server_name" extension.  (This is required for technical conformance with RFC7540; Section 9.2.)  The client MUST NOT send a "cached_info" extension RFC7924 with a CachedObject entry whose CachedInformationType is "cert". 5.1.2. If the server negotiates TLS 1.3 or above and provides an "encrypted_server_name" extension in EncryptedExtensions, the client then processes the extension's "response_type" field: If the value is "esni_accept", the client MUST check that the extension's "nonce" field matches ClientESNIInner.nonce and otherwise abort the connection with an "illegal_parameter" alert. The client then proceeds with the connection as usual, verifying the certificate against the desired name. If the value is "esni_retry_request", the client proceeds with the handshake, verifying the certificate against ESNIKeys.public_name as described in verify-public-name.  If verification or the handshake fails, the client MUST return a failure to the calling application.  It MUST NOT use the retry keys. Otherwise, when the handshake completes successfully with the public name verified, the client MUST abort the connection with an "esni_required" alert.  It then processes the "retry_keys" field from the server's "encrypted_server_name" extension. If one of the values contains a version supported by the client, it can regard the ESNI keys as securely replaced by the server. It SHOULD retry the handshake with a new transport connection, using that value to encrypt the SNI.  The value may only be applied to the retry connection.  The client MUST continue to use the previously-advertised keys for subsequent connections.  This avoids introducing pinning concerns or a tracking vector, should a malicious server present client-specific retry keys to identify clients. If none of the values provided in "retry_keys" contains a supported version, the client can regard ESNI as securely disabled by the server.  As below, it SHOULD then retry the handshake with a new transport connection and ESNI 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_server_name" extension in EncryptedExtensions, the client proceeds with the handshake, verifying the certificate against ESNIKeys.public_name as described in verify-public-name.  The client MUST NOT enable the False Start optimization RFC7918 for this handshake.  If verification 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 ESNI. Otherwise, when the handshake completes successfully with the public name verified, the client MUST abort the connection with an "esni_required" alert.  The client can then regard ESNI as securely disabled by the server.  It SHOULD retry the handshake with a new transport connection and ESNI disabled. [[TODO: Key replacement is significantly less scary than saying that ESNI-naive servers bounce ESNI off.  Is it worth defining a strict mode toggle in the ESNI keys, for a deployment to indicate it is ready for that? ]] Clients SHOULD implement a limit on retries caused by "esni_retry_request" or servers which do not acknowledge the "encrypted_server_name" extension.  If the client does not retry in either scenario, it MUST report an error to the calling application. 5.1.3. When the server cannot decrypt or does not process the "encrypted_server_name" extension, it continues with the handshake using the cleartext "server_name" extension instead (see server- behavior).  Clients that offer ESNI then verify the certificate with the public name, as follows: If the server resumed a session or negotiated a session that did not use a certificate for authentication, the client MUST abort the connection with an "illegal_parameter" alert.  This case is invalid because send-esni requires the client to only offer ESNI- established sessions, and server-behavior requires the server to decline ESNI-established sessions if it did not accept ESNI. The client MUST verify that the certificate is valid for ESNIKeys.public_name.  If invalid, it MUST abort the connection with the appropriate alert. If the server requests a client certificate, the client MUST respond with an empty Certificate message, denoting no client certificate. </ins>  <ins> Note that verifying a connection for the public name does not verify it for the origin.  The TLS implementation MUST NOT report such connections as successful to the application.  It additionally MUST ignore all session tickets and session IDs presented by the server. These connections are only used to trigger retries, as described in handle-server-response.  This may be implemented, for instance, by reporting a failed connection with a dedicated error code. </ins> 5.2. Upon receiving an "encrypted_server_name" extension, the client-  <ins> facing server MUST check that it is able to negotiate TLS 1.3 or greater.  If not, it MUST abort the connection with a "handshake_failure" alert. If the ClientEncryptedSNI.record_digest value does not match the cryptographic hash of any known ESNIKeys structure, it MUST ignore the extension and proceed with the connection, with the following added behavior: It MUST include the "encrypted_server_name" extension in EncryptedExtensions message with the "response_type" field set to "esni_retry_requested" and the "retry_keys" field set to one or more ESNIKeys structures with up-to-date keys.  Servers MAY supply multiple ESNIKeys values of different versions.  This allows a server to support multiple versions at once. The server MUST ignore all PSK identities in the ClientHello which correspond to ESNI PSKs.  ESNI PSKs offered by the client are associated with the ESNI name.  The server was unable to decrypt then ESNI name, so it should not resume them when using the cleartext SNI name.  This restriction allows a client to reject resumptions in verify-public-name. If the ClientEncryptedSNI.record_digest value does match the cryptographic hash of a known ESNIKeys, the server performs the following checks: </ins> If the ClientEncryptedSNI.key_share group does not match one in the ESNIKeys.keys, it MUST abort the connection with an such that its length equals the size of the largest possible Certificate (message) covered by the same ESNI key.  Moreover, the server MUST include the "encrypted_server_name" extension in  <ins> EncryptedExtensions with the "response_type" field set to "esni_accept" and the "nonce" field set to the decrypted PaddedServerNameList.nonce value from the client "encrypted_server_name" extension. If the server sends a NewSessionTicket message, the corresponding ESNI PSK MUST be ignored by all other servers in the deployment when not negotiating ESNI, including servers which do not implement this specification.  This may be implemented by adding a new field to the server session state which earlier implementations cannot parse. This restriction provides robustness for rollbacks (see misconfiguration). </ins> 5.4. to the backend server.  Thus, backend servers function the same as servers operating in Shared Mode. <ins> As in Shared Mode, if the backend server sends a NewSessionTicket message, the corresponding ESNI PSK MUST be ignored by other servers in the deployment when not negotiating ESNI, including servers which do not implement this specification. </ins> 6.  <ins> Unlike most TLS extensions, placing the SNI value in an ESNI extension is not interoperable with existing servers, which expect the value in the existing cleartext extension.  Thus server operators SHOULD ensure servers understand a given set of ESNI keys before advertising them.  Additionally, servers SHOULD retain support for any previously-advertised keys for the duration of their validity. However, in more complex deployment scenarios, this may be difficult to fully guarantee.  Thus this protocol was designed to be robust in case of inconsistencies between systems that advertise ESNI keys and servers, at the cost of extra round-trips due to a retry.  Two specific scenarios are detailed below. </ins> 6.1.  <ins> It is possible for ESNI advertisements and servers to become inconsistent.  This may occur, for instance, from DNS misconfiguration, caching issues, or an incomplete rollout in a multi-server deployment.  This may also occur if a server loses its ESNI keys, or if a deployment of ESNI must be rolled back on the server. The retry mechanism repairs most such inconsistencies.  If server and advertised keys mismatch, the server will respond with esni_retry_requested.  If the server does not understand the "encrypted_server_name" extension at all, it will ignore it as required by RFC8446; Section 4.1.2.  Provided the server can present a certificate valid for the public name, the client can safely retry with updated settings, as described in handle-server-response. Unless ESNI is disabled as a result of successfully establishing a connection to the public name, the client MUST NOT fall back to cleartext SNI, as this allows a network attacker to disclose the SNI. It 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 extension.  RFC8446; Section 9.3 requires that such proxies remove  <ins> any extensions they do not understand.  The handshake will then present a certificate based on the public name, without echoing the "encrypted_server_name" extension to the client. Depending on whether the client is configured to accept the proxy's certificate as authoritative for the public name, this may trigger the retry logic described in handle-server-response or result in a connection failure.  A proxy which is not authoritative for the public name cannot forge a signal to disable ESNI. A non-conformant MITM proxy which instead forwards the ESNI extension, substituting its own KeyShare value, will result in the client-facing server recognizing the key, but failing to decrypt the SNI.  This causes a hard failure.  Clients SHOULD NOT attempt to repair the connection in this case. </ins> 7. operating in Split Mode is not possible.  See cleartext-dns for more details regarding cleartext DNS. <ins> Validating the ESNIKeys structure additionally validates the public name.  This validates any retry signals from the server because the client validates the server certificate against the public name before retrying. </ins> 7.2.8. This design has no impact on application layer protocol negotiation. 8.1.  <ins> 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, EE", and "Recommended" column being set to "Yes". 8.2. <ins> IANA is requested to create an entry, esni_required(121) in the existing registry for Alerts (defined in RFC8446), with the "DTLS-OK" column being set to "Y". 8.3. </ins> 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


If the value is "esni_accept", the client MUST check that the extension's "nonce" field matches ClientESNIInner.nonce and otherwise abort the connection with an "illegal_parameter" alert. <del> The client then proceeds with the connection as usual, verifying the certificate against the desired name. </del>  If the value is "esni_retry_request", the client proceeds with the <del> handshake, verifying the certificate against ESNIKeys.public_name as described in verify-public-name.  If verification or the handshake fails, the client MUST return a failure to the calling application.  It MUST NOT use the retry keys. </del>  Otherwise, when the handshake completes successfully with the <del> public name verified, the client MUST abort the connection with an "esni_required" alert.  It then processes the "retry_keys" field from the server's "encrypted_server_name" extension. </del>  If one of the values contains a version supported by the client, it can regard the ESNI keys as securely replaced by the server. If the server negotiates an earlier version of TLS, or if it does not provide an "encrypted_server_name" extension in EncryptedExtensions, <del> the client proceeds with the handshake, verifying the certificate against ESNIKeys.public_name as described in verify-public-name.  The client MUST NOT enable the False Start optimization RFC7918 for this handshake.  If verification 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 ESNI. </del>  Otherwise, when the handshake completes successfully with the public <del> name verified, the client MUST abort the connection with an </del>  "esni_required" alert.  The client can then regard ESNI as securely disabled by the server.  It SHOULD retry the handshake with a new transport connection and ESNI disabled. When the server cannot decrypt or does not process the "encrypted_server_name" extension, it continues with the handshake using the cleartext "server_name" extension instead (see server- <del> behavior).  Clients that offer ESNI then verify the certificate with the public name, as follows: </del>  If the server resumed a session or negotiated a session that did not use a certificate for authentication, the client MUST abort respond with an empty Certificate message, denoting no client certificate. <del> Note that verifying a connection for the public name does not verify it for the origin.  The TLS implementation MUST NOT report such connections as successful to the application.  It additionally MUST ignore all session tickets and session IDs presented by the server. These connections are only used to trigger retries, as described in handle-server-response.  This may be implemented, for instance, by reporting a failed connection with a dedicated error code. </del>  5.2. associated with the ESNI name.  The server was unable to decrypt then ESNI name, so it should not resume them when using the cleartext SNI name.  This restriction allows a client to reject <del> resumptions in verify-public-name. </del>  If the ClientEncryptedSNI.record_digest value does match the cryptographic hash of a known ESNIKeys, the server performs the If the server sends a NewSessionTicket message, the corresponding ESNI PSK MUST be ignored by all other servers in the deployment when not negotiating ESNI, including servers which do not implement this <del> specification.  This may be implemented by adding a new field to the server session state which earlier implementations cannot parse. </del>  This restriction provides robustness for rollbacks (see misconfiguration). ESNI keys, or if a deployment of ESNI must be rolled back on the server. <del> The retry mechanism repairs most such inconsistencies.  If server and advertised keys mismatch, the server will respond with esni_retry_requested.  If the server does not understand the "encrypted_server_name" extension at all, it will ignore it as required by RFC8446; Section 4.1.2.  Provided the server can present a certificate valid for the public name, the client can safely retry with updated settings, as described in handle-server-response. </del>  Unless ESNI is disabled as a result of successfully establishing a connection to the public name, the client MUST NOT fall back to if all Web browsers start using ESNI, the presence of this value does not signal suspicious behavior to passive eavesdroppers.  7.2.5. This design is not forward secret because the server's ESNI key is

(WG email coming shortly.) Update: This has been rebased to master, now that is merged. The new server behavior ("Improve ESNI robustness") means clients can send GREASE ESNI extensions without interop issues. Write some text recommending this, better meeting the "Do not stick out" criteria.
The GREASE section seems fine to me. (NAME is there other new material in this PR?) NAME NAME please have a look when possible.
It's whatever's in the second commit, so mostly that section but also a bit elsewhere: URL
I share the view that the GREASE section looks fine.
NAME Did you mean to leave the comments on URL (Notably the text already has seen a number of revisions here. I haven't gone back to update this PR yet. This PR was meant to just be the second commit.)
Ugh. Having something which claims to be a merged PR is kind of an attractive nusiance, then, no? Perhaps you can apply the changes here?
In my defense, I split them at your request and did include the following. ;-) But, sure, I can rebase this one. I figured was the bulk of the interesting bits (7401f55013f68ec47dad36b99977ad6aa881d9f1 is much smaller) so I figured we'd first and then I'd update this one at the end.
Sure, and I read that as "this is an accurate reflection of my put for both of these". But, sure, I can rebase this one. I figured Thanks.
Well, it was when I wrote that sentence. :-) Anyway, it's now been rebased and I've incorporated your comments as well.

If the value is "esni_accept", the client MUST check that the extension's "nonce" field matches ClientESNIInner.nonce and otherwise abort the connection with an "illegal_parameter" alert.  <ins> The client then proceeds with the connection as usual, authenticating the connection for the origin server. </ins> If the value is "esni_retry_request", the client proceeds with the  <ins> handshake, authenticating for ESNIKeys.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 use the retry keys. </ins> Otherwise, when the handshake completes successfully with the  <ins> public name authenticated, the client MUST abort the connection with an "esni_required" alert.  It then processes the "retry_keys" field from the server's "encrypted_server_name" extension. </ins> If one of the values contains a version supported by the client, it can regard the ESNI keys as securely replaced by the server. If the server negotiates an earlier version of TLS, or if it does not provide an "encrypted_server_name" extension in EncryptedExtensions,  <ins> the client proceeds with the handshake, authenticating for ESNIKeys.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 ESNI. </ins> Otherwise, when the handshake completes successfully with the public  <ins> name authenticated, the client MUST abort the connection with an </ins> "esni_required" alert.  The client can then regard ESNI as securely disabled by the server.  It SHOULD retry the handshake with a new transport connection and ESNI disabled. When the server cannot decrypt or does not process the "encrypted_server_name" extension, it continues with the handshake using the cleartext "server_name" extension instead (see server-  <ins> behavior).  Clients that offer ESNI then authenticate the connection with the public name, as follows: </ins> If the server resumed a session or negotiated a session that did not use a certificate for authentication, the client MUST abort respond with an empty Certificate message, denoting no client certificate.  <ins> Note that authenticating a connection for the public name does not authenticate it for the origin.  The TLS implementation MUST NOT report such connections as successful to the application.  It additionally MUST ignore all session tickets and session IDs presented by the server.  These connections are only used to trigger retries, as described in handle-server-response.  This may be implemented, for instance, by reporting a failed connection with a dedicated error code. 5.1.4. If the client attempts to connect to a server and does not have an ESNIKeys structure available for the server, it SHOULD send a GREASE I-D.ietf-tls-grease "encrypted_server_name" extension as follows: Select a supported cipher suite, named group, and padded_length value.  The padded_length value SHOULD be 260.  Set the "suite" field to the selected cipher suite. Set the "key_share" field to a randomly-generated valid public key for the named group. Set the "record_digest" field to a randomly-generated string of hash_length bytes, where hash_length is the length of the hash function associated with the chosen cipher suite. Set the "encrypted_sni" field to a randomly-generated string of 16 + padded_length + tag_length bytes, where tag_length is the tag length of the chosen cipher suite's associated AEAD. If the server sends an "encrypted_server_name" extension, the client MUST check the extension syntactically and abort the connection with a "decode_error" alert if it is invalid.  If the "response_type" field contains "esni_retry_requested", the client MUST ignore the extension and proceed with the handshake.  If it contains "esni_accept" or any other value, the client MUST abort the connection with an "illegal_parameter" alert. Offering a GREASE extension is not considered offering an encrypted SNI for purposes of requirements in client-behavior.  In particular, the client MAY offer to resume sessions established without ESNI. </ins> 5.2. associated with the ESNI name.  The server was unable to decrypt then ESNI name, so it should not resume them when using the cleartext SNI name.  This restriction allows a client to reject  <ins> resumptions in auth-public-name. Note that an unrecognized ClientEncryptedSNI.record_digest value may be a GREASE ESNI extension (see grease-extensions), so it is necessary for servers to proceed with the connection and rely on the client to abort if ESNI was required.  In particular, the unrecognized value alone does not indicate a misconfigured ESNI advertisement (misconfiguration).  Instead, servers can measure occurrences of the "esni_required" alert to detect this case. </ins> If the ClientEncryptedSNI.record_digest value does match the cryptographic hash of a known ESNIKeys, the server performs the If the server sends a NewSessionTicket message, the corresponding ESNI PSK MUST be ignored by all other servers in the deployment when not negotiating ESNI, including servers which do not implement this  <ins> specification. </ins> This restriction provides robustness for rollbacks (see misconfiguration). ESNI keys, or if a deployment of ESNI must be rolled back on the server.  <ins> The retry mechanism repairs inconsistencies, provided the server is authoritative for the public name.  If server and advertised keys mismatch, the server will respond with esni_retry_requested.  If the server does not understand the "encrypted_server_name" extension at all, it will ignore it as required by RFC8446; Section 4.1.2. Provided the server can present a certificate valid for the public name, the client can safely retry with updated settings, as described in handle-server-response. </ins> Unless ESNI is disabled as a result of successfully establishing a connection to the public name, the client MUST NOT fall back to if all Web browsers start using ESNI, the presence of this value does not signal suspicious behavior to passive eavesdroppers. <ins> Additionally, this specification allows for clients to send GREASE ESNI extensions (see grease-extensions), which helps ensure the ecosystem handles the values correctly. </ins> 7.2.5. This design is not forward secret because the server's ESNI key is


The client MAY either omit the "server_name" extension or provide an innocuous dummy one (this is required for technical conformance with <del> RFC7540; Section 9.2.) </del>  If the server does not negotiate TLS 1.3 or above, then the client MUST abort the connection with an "unsupported_version" alert.  If

Addresses .
An adversary who observes the certificate fingerprint could confirm certificates previously fetched by a ESNI-compatible client.
Fixed in .

The client MAY either omit the "server_name" extension or provide an innocuous dummy one (this is required for technical conformance with  <ins> RFC7540; Section 9.2.).  The client MUST NOT send a "cached_info" extension RFC7924 with a CachedObject entry whose CachedInformationType is "cert". </ins> If the server does not negotiate TLS 1.3 or above, then the client MUST abort the connection with an "unsupported_version" alert.  If


4. <del> SNI Encryption keys can be published in the DNS using the ESNIKeys structure, defined below. </del>  The version of the structure.  For this specification, that value SHALL be 0xff02.  Clients MUST ignore any ESNIKeys structure with A list of extensions that the client can take into consideration when generating a Client Hello message.  The format is defined in RFC8446; Section 4.2.  The purpose of the field is to provide room <del> 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 cipher suite list is orthogonal to the list of keys, so each key may <del> be used with any cipher suite. </del>  This structure is placed in the RRData section of an ESNI record as- is.  Servers MAY supply multiple ESNIKeys values, either of the same query domain name's canonicalized form.  That is, if a client queries example.com, the ESNI Resource Record might be: <del> Servers MUST ensure that if multiple A or AAAA records are returned for a domain with ESNI support, all the servers pointed to by those records are able to handle the keys returned as part of an ESNI record for that domain. Clients obtain these records by querying DNS for ESNI-enabled server 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. In cases where the domain of the A or AAAA records being resolved do not match the SNI Server Name, such as when RFC7838 Section 2.3 is being used, the SNI domain should be used for querying the ESNI record. 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 on a home router. Note that the length of this structure MUST NOT exceed 2^16 - 1, as the RDLENGTH is only 16 bits RFC1035.  5. The encrypted SNI is carried in an "encrypted_server_name" extension,

This change does the following: Add support for mandatory extensions, which are those that clients must process else reject the ESNIKeys record. Add a mandatory extension carrying a set of v4/v6 addresses, which is effectively the combined record mode. Add text describing how clients ought to resolve ESNI and A/AAAA records, and use the results to initiate TLS connections.
That's fair. However, it seems to be the price to get this sort of simplicity. Moreover, as an extension, it's not pressed upon all clients for all providers. Also true! I'm hoping that folks who are in the position and capable of vending these records can comment on this design's cost.
Given that there was no serious objection to this change and that it works for at least one operator, I'm merging this PR. We can continue to iterate on as needed in parallel.
issues and advocate for the removal of the _esni prefix. A significant side effect of that is that it becomes impossible to use a CNAME at the apex of a zone in order to refer to ESNIKeys of a server operated elsewhere. Inlining keys, as is often done with A/AAAA at the apex, is not plausible due to key rotation. The issue can largely be mitigated with the introduction of an inclusion mechanism directly in the TXT/ESNI record.
This should be fixed by and follow up changes. Closing as such. Please reopen if necessary!
I don't believe resolves this. 136 effectively requires the address and ESNI information to be stapled together (which I still find objectionable). The issue here is that the apex cannot use a CNAME to delegate to the actual third party provider's ESNI record.. typically it just includes an A/AAAA record at the apex and hopes the pointer doesn't go stale. That's bad enough for A/AAAA but it would be terrible for ESNI because it means copying keying information and hoping it doesn't go stale between two different entities. NAME can you reopen?
Sure! Sorry for the misunderstanding. We can probably update this PR to make use of the new DNS-specific extensions.
Fixed by .
This is a substantial step in the right direction, but the fact that A/AAAA records are basically ignored makes me uneasy. The fact that A/AAAA results are essentially fully embedded would imply that all logic involved in selectively returning those responses has to be duplicated for ESNIKeys, which is also sub-optimal.

4.  <ins> Publishing ESNI keys in the DNS requires care to ensure correct behavior.  There are deployment environments in which a domain is served by multiple server operators who do not manage the ESNI Keys. Because ESNIKeys and A/AAAA lookup are independent, it is therefore possible to obtain an ESNIKeys record which does not match the A/AAAA records.  (That is, the host to which an A or AAAA record refers is not in possession of the ESNI keys.)  The design of the system must therefore allow clients to detect and recover from this situation. 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. The following sections describe a DNS record format that achieve these goals. 4.1. SNI Encryption keys can be published using the following ESNIKeys structure. </ins> The version of the structure.  For this specification, that value SHALL be 0xff02.  Clients MUST ignore any ESNIKeys structure with A list of extensions that the client can take into consideration when generating a Client Hello message.  The format is defined in RFC8446; Section 4.2.  The purpose of the field is to provide room  <ins> for additional features in the future.  An extension may be tagged as mandatory by using an extension type codepoint with the high order bit set to 1.  A client which receives a mandatory extension they do not understand must reject the record. </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 cipher suite list is orthogonal to the list of keys, so each key may  <ins> be used with any cipher suite.  Clients MUST parse the extension list and check for unsupported mandatory extensions.  If an unsupported mandatory extension is present, clients MUST reject the ESNIKeys record. </ins> This structure is placed in the RRData section of an ESNI record as- is.  Servers MAY supply multiple ESNIKeys values, either of the same query domain name's canonicalized form.  That is, if a client queries example.com, the ESNI Resource Record might be:  The "checksum" field provides protection against transmission errors, including those caused by intermediaries such as a DNS proxy running on a home router. Note that the length of this structure MUST NOT exceed 2^16 - 1, as the RDLENGTH is only 16 bits RFC1035. <ins> 4.2. This section describes a client ESNI resolution algorithm using a new "address_set" extension described below.  Future specifications may introduce new extensions and corresponding resolution algorithms. 4.2.1. ESNIKeys records MAY indicate a specific IP address(es) for the host(s) in possession of the ESNI private key via the following mandatory "address_set" ESNIKeys extension: The body of this extension is encoded using the following structure. A set of Address structures containing IPv4 or IPv6 addresses to hosts which have the corresponding private ESNI key. 4.2.2. Clients obtain ESNI records by querying the DNS for ESNI-enabled server domains.  In cases where the domain of the A or AAAA records being resolved do not match the SNI Server Name, such as when RFC7838 is being used, the alternate domain should be used for querying the ESNI TXT record.  (See Section 2.3 of RFC7838 for more details.) Clients SHOULD initiate ESNI queries in parallel alongside normal A or AAAA queries to obtain address information in a timely manner in the event that ESNI is available.  The following algorithm describes a procedure by which clients can process ESNIKeys responses as they arrive to produce addresses for ESNI-capable hosts. CD (Connection Delay) is a configurable parameter.  The recommended value is 50 milliseconds, as per the guidance in RFC8305. </ins> 5. The encrypted SNI is carried in an "encrypted_server_name" extension,