Draft: draft-ietf-tls-esni

Commentary: This is an alternative to , which constrains the client such this suggestion is feasible : thoughts ? This does constrain the client behavior , though our draft implementation would not be affected by it . ( We run through all the extensions in our usual order and compute the inner value + whether to compress it . If we intend to compress it , we write it to a separate buffer instead of the pending ClientHelloInner . Then we drop in all the buffered extensions , so they 're contiguous and , in the EncodedClientHelloInner , write echouter_extensions instead of the buffer . This process keeps the relative order of compressed extensions , so we follow this rule anyway . ) 
 Same here ( wrt liking:- ) . Cheers ,    to appendix . 
 can you please rebase ? 
 Rebased 
 A naive implementation of decoding the inner ClientHello can easily go quadratic when processing outerextensions . Thus far , nothing in TLS has required searching for O(N ) extensions , only O(implemented extensions ) . It 's likely a lot of TLS implementations handle the extensions list linearly most of the time . outerextensions is now the first time you have to search for extensions you may not have implemented . A naive implementation would then go quadratic , which is a server DoS risk . It does n't need to go quadratic ( for instance , you can sort ClientHelloOuter 's extensions and then binary search per lookup ) , but it 's probably worth at least calling this out , if not switching to a simpler decoding scheme . ( For a simpler decoder , we already require CHInner group the compressed extensions consecutively . We could do the same with CHOuter , at which point we only need to reference one byte slice , be it by offset / length or some goofier scheme . ) 
 Switching to a simpler scheme : yes please . Just dropping the compression idea entirely : even better:- ) Given this optimisation is really mostly for PQ schemes , I 'd argue it 'd be better to just drop the current idea and have a more constrained one , e.g. using a bit mask as the value of outer_extensions , so implementation know exactly which extensions can ever be copied from the outer CH . And I 'd further argue we can leave defining all of that until later too . The current scheme also imposes quite a testing burden . At least with OpenSSL most extensions have some side - effect and testing that all combinations of those are handled correctly for the inner CH is n't trivial . ( That 's partly down to old code being a bit awful but I guess wo n't be simple for any code base . ) Cheers , S. 
 Not sure I follow the concern with side effects . Are you processing the extensions as part of decompression ? In order to make the handshake work , you 'd have to construct the ClientHello by bytes anyway . Thus it seems the natural implementation would be to resolve ECH early , before ClientHello processing , then compute the inner ClientHello as bytes , then run the handshake with the resulting ClientHello . This aligns nicely with the model in split mode , etc . The issue I have in mind is simply that the decoding process , a pure function from a pair of byte strings to a byte string , is easy to accidentally make inefficient . 
 It occurs to me this might cause some conflicts between wanting ECH - ful and ECH - less ClientHellos to look similar ( do n't stick out ) and pressures on ClientHello extension order . The order should be irrelevant , but compat concerns have cropped up before . ( If only we required extensions be sorted ... ) Another possibility for a simpler decode would be if outer_extensions listed indexes into the extension list , rather than extension IDs . Though we would need to answer silly questions like whether it 's an index into ClientHelloOuter or ClientHelloOuterAAD . 
 Since this is more of an implementation detail , let 's just add a warning to handle this responsibly ( i.e. the sort and binary search ) . 
 Yet another way to address this is with a simple hash map , e.g. : My vote is to leave the encoding scheme as - is and simply leave a warning . 
 could one of you please propose text for that warning ? 
 Another DoS risk to be careful of : implementations typically enforce size limits on TLS messages so they are n't forced to buffer 2 ^ 24 bytes . Whatever size limit is enforced on ClientHello needs to also be enforced on the inner ClientHello before allocating memory for it . In particular , if  has a single giant extension , and it is referenced multiple times in OuterExtensions , you can blow up the server 's resource usage . ( Though I ca n't think of any case without repeating an extension where the resulting size grows dramatically . ) 
 Is there any valid reason why an extension would appear multiple times in a ClientHello ? If not , maybe we should specify that the server must abort if OuterExtensions references an extension more than once . 
 Seems reasonable . I think the combination of " no duplicate extensions " and " no referencing the ECH extension " imply that sizeof(ClientHelloInner ) < sizeof(ClientHelloOuter ) . 
 Keeping this opening to track and comments on CH sizes . could you please send a PR if you have cycles ? 
 Discussed in 110 : Will propose text to address the second vector and then close this issue . 
 I 'm not going to nitpick this , but I like it . We currently have the same triply - nested loop here : the outer loop iterates over extensions in CHInner for most extensions we just copy them into a new structure for echouter we have two more layers of loop the middle loop iterates over the items in echouter the inner loop iterates over CHOuter , looking for a match This would n't really reduce code complexity , but it would mean that we would n't need to start over every time for that innermost loop . So we would be able to run in linear time as this says . Like you , as we iterate over extensions in the same order when constructing messages , this would n't have any effect on generating a ClientHello . My only concern with this is that it is somewhat more prescriptive than I would generally like for a specification . Moving this algorithmic stuff to an appendix might be a good balance . Either way , if you are adding this much text , it needs a section heading . LGTM pending suggestions!Works for me !
Paragraph (version A):
OuterExtensions consists of one or more ExtensionType values, each of
which reference an extension in ClientHelloOuter.
Paragraph (version B):
OuterExtensions consists of one or more ExtensionType values, each of
which reference an extension in ClientHelloOuter.  The extensions in
OuterExtensions MUST appear in ClientHelloOuter in the same relative
order, however, there is no requirement that they be continguous.
For example, OuterExtensions may contain extensions A, B, C, while
ClientHelloOuter contains extensions A, D, B, C, E, F.
Paragraph (version B):
The "ech_outer_extensions" extension is only used for compressing the
ClientHelloInner.  It can only be included in
EncodedClientHelloInner, and MUST NOT be sent in either
ClientHelloOuter or ClientHelloInner.
Paragraph (version A):
The client-facing server computes ClientHelloInner by reversing this
process.  First it makes a copy of EncodedClientHelloInner and copies
the legacy_session_id field from ClientHelloOuter.  It then looks for
an "ech_outer_extensions" extension.  If found, it replaces the
extension with the corresponding sequence of extensions in the
ClientHelloOuter.  If any referenced extensions are missing or if
"encrypted_client_hello" appears in the list, the server MUST abort
the connection with an "illegal_parameter" alert.
Paragraph (version A):
The "ech_outer_extensions" extension is only used for compressing the
ClientHelloInner.  It MUST NOT be sent in either ClientHelloOuter or
ClientHelloInner.
Paragraph (version A):
Note that it is possible to implement decoding of the
EncodedClientHelloInner in a way that creates a denial-of-service
vulnerability.  Specifically, the server needs to check that each
extension in the OuterExtensions list appears in the
ClientHelloOuter.  The naive strategy would require O(N*M) time,
where N is the number of extensions in the ClientHelloOuter and M is
the number of extensions in the OuterExtensions list.  Malicious
clients could exploit this behavior in order to cause excessive work
for the server, possibly making it unavailable.  This problem can be
mitigated by representing OuterExtensions in a way that allows it to
be searched more quickly.  For example, the runtime can be improved
to O(N*log(M)) by sorting the OuterExtensions and using binary search
to access it.
Paragraph (version B):
Any referenced extension is missing in ClientHelloOuter.
Paragraph (version B):
"encrypted_client_hello" appears in OuterExtensions.
Paragraph (version B):
OuterExtensions contains duplicate values.
Paragraph (version B):
The extensions in ClientHelloOuter corresponding to those in
OuterExtensions do not occur in the same order.
Paragraph (version B):
Implementations SHOULD bound the time to compute a ClientHelloInner
proportionally to the ClientHelloOuter size.  If the cost are
disproportionately large, a malicious client could exploit this in a
denial of service attack.  linear-outer-extensions describes a
linear-time procedure that may be used for this purpose.
Paragraph (version A):
If it compressed any extensions in EncodedClientHelloInner, it
MUST copy the corresponding extensions from ClientHelloInner.
Paragraph (version B):
If it compressed any extensions in EncodedClientHelloInner, it
MUST copy the corresponding extensions from ClientHelloInner.  The
copied extensions additionally MUST be in the same relative order
as in ClientHelloInner.