Subject: Re: [TLS] Is stateless HelloRetryRequest worthwhile? (was Re: TLS 1.3 Problem?)

From: "Salz, Rich" <[email-masked]>
Date: Thu, 1 Oct 2020 11:09:50 +0000

> Not always; see TCP "fast open" options.

>    Maybe this should be disabled?  Fortunately if you wanted
    to there is a setsockopt for TCP_FASTOPEN.
 
Yeah, that will go over great for the folks who want to DDoS you. :)

---

From: "Michael D'Errico" <[email-masked]>
Date: Thu, 01 Oct 2020 10:02:37 -0400

> I am having a difficult time understanding the tradeoffs you're facing.

This is the first time I'm reading the TLS 1.3 RFC.  I have
implemented SSLv3, TLS 1.0, 1.1, and 1.2.  You may have
used my test server at https www dot mikestoolbox dot
org or dot net to test your own code.  It's kind of old now
since it doesn't do ECC and the DHE_RSA key exchange
I focused on has been disabled by most clients so you
end up getting a regular RSA handshake now.

I have gotten caught by the stateless HelloRetryRequest
and can't get past it.  You can't possibly implement it the
way the spec suggests with just a hash in a HRR cookie
extension.  If it can be done at all, the stateless server
should probably just put the ClientHello1 and HRR (minus
the cookie) into the cookie extension.  If this is how it
should be done, then the spec should say so -- exactly
how to do it so everyone does it the same (correct) way
and not just hand-wave it and say figure it out yourself.

Getting the cookie right isn't enough because of the
potential for resending an old cookie by a mischievous
client.  Nico suggests that replay caches are hard to
get right even when your distributed servers are all
talking to each other.

Mike

---

From: Nick Harper <[email-masked]>
Date: Thu, 1 Oct 2020 14:08:55 -0700

On Thu, Oct 1, 2020 at 7:05 AM Michael D'Errico <[email-masked]> wrote:

> extension.

The only thing the server needs to know is the hash of the ClientHello (so
it can restore the transcript hash) and that the server has already sent a
HelloRetryRequest (which it can detect by presence of the cookie). The only
argument I've seen made for what the spec suggests not working is being
able to verify which fields changed between ClientHello1 and ClientHello2.
I see no language in RFC 8446 that the server MUST enforce that the
ClientHello2 is conformant with respect to ClientHello1. Thus, I think what
the spec suggests should work.

> If it can be done at all, the stateless server
> client.

The cookie serves as an alternative way for the server to remember the
ClientHello1 sent by the server. If the client is trying to perform some
sort of attack on the server by re-sending an old cookie, I assume that a
prerequisite for this attack is that the TLS handshake succeeds. For the
handshake to succeed, the client needs to know the ClientHello1 that
corresponds to the cookie so that it can compute the transcript hash
correctly. Regardless of the source of that client hello, the client can
equivalently send that ClientHello, get a real HelloRetryRequest from the
server, and send its ClientHello2, or it can send a ClientHello with the
cookie, bypassing the step of waiting for the HRR. A client attempting to
do something malicious with an HRR cookie is equivalent to behavior that
does not depend on a cookie.

>
> Mike
>
> _______________________________________________
> TLS mailing list
> [email-masked]
> https://www.ietf.org/mailman/listinfo/tls
>

---

From: "Michael D'Errico" <[email-masked]>
Date: Fri, 02 Oct 2020 14:15:48 -0400

> > You can't possibly implement [stateless HelloRetryRequest] the
> > way the spec suggests with just a hash in a HRR cookie extension. 
> 
> The only thing the server needs to know is the hash of the ClientHello 
> (so it can restore the transcript hash) and that the server has already 
> sent a HelloRetryRequest (which it can detect by presence of the 
> cookie). The only argument I've seen made for what the spec suggests 
> not working is being able to verify which fields changed between 
> ClientHello1 and ClientHello2.

The server also needs to know the entire HelloRetryRequest message
since this goes into the Transcript Hash calculation:

  Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) =
      Hash(message_hash ||        /* Handshake type */
           00 00 Hash.length  ||  /* Handshake message length (bytes) */
           Hash(ClientHello1) ||  /* Hash of ClientHello1 */
           HelloRetryRequest  || ... || Mn)

Many of the fields in HelloRetryRequest are fixed or predictable, but
the legacy_session_id_echo is not, for example.  Also, relying on the
client to remind you what the hash of ClientHello1 is seems extremely
"optimistic" (in my opinion).

> I see no language in RFC 8446 that the server MUST enforce that
> the ClientHello2 is conformant with respect to  ClientHello1.

It doesn't, and you could probably argue that the HelloRetryRequest
can be just a "do over" and let the client try again however it wants.

But if the server doesn't do 100% validation of the second ClientHello
as if it never received the first one, then there is room for mischief by
a "curious" client.

Mike

---

From: "Michael D'Errico" <[email-masked]>
Date: Fri, 02 Oct 2020 14:46:55 -0400

> If the client is trying to perform 
> some sort of attack on the server by re-sending an old cookie, I assume 
> that a prerequisite for this attack is that the TLS handshake succeeds.

Maybe you don't need the handshake to succeed?  As a non-cryptographer
I can't say what the implications might be (as I said to Watson in private
email) but some of you are crypto people.  If you can create N parallel
sessions using the same cookie (send the same ClientHello1 with the
same Random value (?), or maybe trick a poorly-written server by sending
an initial ClientHello1 containing a cookie extension, or use DTLS (?)), what
could you do as a malicious client?  I don't know the answer, I'm asking.

Mike

---

From: Michael D'Errico <[email-masked]>
Date: Fri, 2 Oct 2020 22:13:14 -0400

>             HelloRetryRequest  || ... || Mn)

Please don't tell me all the current TLS 1.3 implementations
forgot to include the HelloRetryRequest in the transcript hash.

Mike

---

From: Michael D'Errico <[email-masked]>
Date: Sat, 3 Oct 2020 10:21:44 -0400

> forgot to include the HelloRetryRequest in the transcript hash.

Is this the reason why Microsoft ditched Edge and rebuilt on
top of Chrome?  There must have been a bug bounty for this.
Are there still bug bounties?  I've been sidelined for a while....

Mike

---

From: Nick Lamb <[email-masked]>
Date: Sat, 3 Oct 2020 21:12:57 +0100

On Fri, 02 Oct 2020 14:15:48 -0400

> > > way the spec suggests with just a hash in a HRR cookie extension.

Lots of people have and it works just fine, so it seems to me that "You
can't possibly" here means something closer to "I still don't
understand how to" and as such would be more appropriate to some sort
of programming Q&A site like Stack Overflow than an IETF working group.

> "optimistic" (in my opinion).

The client MUST use the same value for legacy_session_id in its retried
ClientHello. As a result this value will be available alongside the
cookie.

Section 4.4.2 is clear that a hash used this way in the cookie should be
"protected with some suitable integrity protection algorithm". For
example some implementations use an HMAC construction, but you could do
other things here successfully. So in fact this is not especially
optimistic.

Nick.

---

From: Michael D'Errico <[email-masked]>
Date: Sat, 3 Oct 2020 17:04:28 -0400

> of programming Q&A site like Stack Overflow than an IETF working group.

StackOverflow has only one result if you search for HelloRetryRequest
and it is about jdk.disabledAlgorithms.

When you say it "works just fine" I think you are saying that the
handshake will complete.  But is it secure?  This is the important
parts of "works" and I'm not sure it's possible to do it correctly
without a lot of work, and am certain that a hash is not enough
information even if it's integrity-protected.

> other things here successfully. So in fact this is not especially

All the integrity protection bytes tell you is that the server did in fact
generate the cookie at some point in the past.  You don't even know
how old the cookie is if it just contains a hash, unless you are
frequently changing the key (and keeping the previous key around for
a bit to cover the case that the key rollover happened within the life-
time of an old cookie).  A client could possibly exploit this by making
many connections and sending a second ClientHello on each one with
the same legacy_session_id_echo and cookie.  A stateless server might
not be keeping track of whether a cookie is being reused, and why
would it if it's actually stateless?  If it's going to go through the 
trouble
of keeping track of previously-used cookies, possibly across several
distributed machines, why not just be not-stateless and avoid all of
the hassle?

And, yes, it is optimistic to trust a client to do the right thing. It's 
also
dangerous.  I used the word optimistic to downplay the significance.

Rebuilding the beginning of the transcript hash based solely on what
the client sends in its second ClientHello message is fraught with peril.

Mike

---

From: Luke Curley <[email-masked]>
Date: Sun, 4 Oct 2020 13:35:00 -0700

Mike,

The server receives a ClientHello from a prospective client. In the case of
DTLS and QUIC, this path has not yet been validated, so the server may want
to verify the remote address prior to continuing the handshake. This is
incredibly important as generating key_shares and signing the transcript
are not trivial operations and make for a very easy DDoS attack.

The server hashes the ClientHello message and generates a random cookie. In
a connection-oriented setup, the cookie is just a random challenge. In a
semi-stateful environment, there's a lookup table from the cookie to the
ClientHello hash and no other state is stored. In a stateless environment,
the cookie might be an encrypted payload, containing the ClientHello hash
plus any additional information.

This is primarily designed with load balancers in mind. The connection is
validated by front-line load balancers that mitigate DDoS attacks and
distribute "safe" connections to backend servers. These load balancers deal
with an enormous amount of traffic and must be simple: 1) reply to
ClientHello messages with a HelloRetryRequest and 2) assign and forward
valid ClientHello retries to a backend server.

The original ClientHello does not need to be stored, as the HRR contains
the information required to continue the handshake. The backend service can
use the cookie to either lookup the CH hash in a shared database, or even
better decrypt the cookie using a shared secret to get the CH hash. Now the
handshake can be completed like normal.

I definitely agree that the benefits of HRR are not made obvious in the RFC
and I thought it was weird too. I wouldn't have hashed the hash of the
ClientHello, but it makes sense given most languages don't allow storing
the state of a hash. The design decisions make sense for services at scale
and require minimal effort for hobby TLS 1.3 implementations (including
mine).