Thanks for the writeup, Martin.

Unfortunately I think we have two goals that are at odds
with each other here:
1) reducing the ability to fingerprint QUIC implementations
2) making ECH indistinguishable from GREASE ECH

Both have value, though on different axes. (1) provides
user privacy (though roughly zero of that, see below)
whereas (2) can help reduce ECH blockage if sufficient
client deployments of ECH enable GREASE ECH.

In practice, as MT points out, implementations are already
fingerprintable using a bunch of other details, such as
initial DCID length. So the benefit to (1) is limited.

I worry about the impact to (2) though. If clients start
using different transport parameters when they have an
ECH config available, it makes ECH GREASE stick out
in such a way that the network can block real ECH while
letting GREASE ECH through.

Is this worth considering? Or have we given up on (2)
for some other reason?

David

On Tue, May 27, 2025 at 2:41 PM Christian Huitema <[email protected]>
wrote:

> Thanks for writing your experience, Martin.
>
> Would it be useful to have some kind of "recommended outer TP" value?
> Having multiple implementations use the same value would reduce
> fingerprinting even more.
>
> -- Christian Huitema
>
> On 5/26/2025 11:11 PM, Martin Thomson wrote:
> > Inspired by some offline discussions, I thought I'd share the outcome of
> my recent attempt to split what our implementation includes in the inner
> and outer ClientHello for ECH.
> >
> > Until recently, we put all the transport parameters in both inner and
> outer ClientHello.  That's the most efficient, as TLS can compress that
> down to basically nothing.  However, there are a few things that don't
> really need to be on the outer envelope and now that we have plenty of
> space to burn thanks to blowing the one-packet limit on PQ key exchange, it
> seemed like a good time to look into what it would take to do better.
> >
> > The core realization is that the outer ClientHello is only good for
> getting the handshake done and a fresh ECH configuration from the server.
> That takes very, very little of QUIC.  Most of the stuff in transport
> parameters is unused if you have to use the outer ClientHello.  No streams
> or datagrams.  Most extensions are inoperative, as they only operate
> post-handshake generally.
> >
> > The way I implemented this was as a simple filter: an inner ClientHello
> has no filter on which transport parameters are included, but the outer
> ClientHello has a filter that discards any unnecessary transport parameters
> as it writes the extension.
> >
> > The filter needs to retain transport parameters that are critical for
> the handshake.  This is a short list: just the connection ID parameters
> that are used to validate connection ID changes due to Retry; plus the
> version negotiation transport parameter that validates any version
> negotiation.  If either of these are absent, connections will be rejected
> by servers (though not always for version negotiation, read on).
> >
> > Trick for our implementation was to avoid having to switch to a
> different connection configuration depending on the choice of ClientHello.
> For instance, we could drop the version negotiation parameter if we
> completely disabled QUIC version 2 if the outer ClientHello is used, but
> that's pretty annoying to get right.  Better to stick with a single
> configuration that works either way.
> >
> > There are three transport parameters that are not clearly one way or the
> other.  You could get away with bad values of these in the case of an ECH
> fallback, but you might not want to:
> >
> > * We set a maximum ACK delay that is different from the default, so if
> we omitted that, we'd be creating a mismatch between what we advertise and
> our actual configuration.  You see, it is probably the case that you could
> rely on defaults for maximum ACK delay here, with the only effect being
> that the RTT estimate for the peer is off by any difference.  That might
> not have serious consequences for a connection that is so short-lived.
> >
> > * The ACK delay exponent is not something we allow to change on our
> side, but it's in the filter because setting it seems safest, even if - by
> the same logic as applies to maximum ACK delay - the consequence isn't
> serious.
> >
> > * The same is true for the UDP payload size, which we don't have
> specific configuration for that would have it vary from the default.  Even
> if we had a cap and were enforcing it, it would look like a constrained MTU
> on the path, which is probably harmless.  The cost being a few lost
> packets, if we ever added a way to reduce this from the default.
> >
> > I ended up playing it safe by including these three transport parameters
> in the filter.  With just one of them (max ACK delay) being written out in
> our current code.  I can see why others might choose to drop them entirely
> if space were an issue.
> >
> > Then, there are all the things that are definitely not needed: anything
> related to streams, flow control, connection IDs (a default of 2 is more
> than enough), preferred address, migrations, idle timeouts, and
> extensions.  The connection isn't around long enough for these to matter.
> >
> > For extensions, we really only have delayed ACK and datagram
> implemented, but others would include multipath and almost every extension
> I can conceive of.  Almost.  There aren't many extensions where the
> transport parameters you send bind you to operate differently.  For
> instance, even with multipath QUIC, you can still send ordinary ACK frames,
> even though the extension strongly encourages the use of PATH_ACK instead
> (that's a good choice, by the way).  We do have the greased QUIC bit
> implemented, which we don't signal support for in the outer ClientHello
> without also turning off the code for decoding packets without the bit
> set.  That is safe to do: no peer is going to abort if you accept a packet
> with the bit cleared...geez, I sure hope they don't...
> >
> > The result is that the handshake gets a little larger (duplicating the
> connection IDs being the main culprit), but it is a fair bit smaller.  I
> hope that this inspires others to do the same work.  It really wasn't that
> hard.
> >
> > For the most part, the effect on privacy is actually zero.  If we
> concede that our handshake is already identifiable as being from this
> particular implementation, the transport parameters we advertise are
> generally the same for any connection we establish.  Still, it makes sense
> to minimize the outward profile, so as not to undermine other efforts (like
> efforts to eliminate TLS extension differences).
> >
> > Most importantly, it clears a path to using the inner ClientHello for
> stuff where privacy really matters.
> >
> > Cheers,
> > Martin
> >
>
>

Reply via email to