Subject: Alternative ways to keeps ODCID on the server

From: Dmitri Tikhonov <[email-masked]>
Date: Fri, 24 Jan 2020 17:24:35 -0500

Hello,

PR 3107 introduced the following text:

   If a server receives a client Initial that can be unprotected but
   contains an invalid Retry token, it knows the client will not accept
   another Retry token.  The server can discard such a packet and allow
   the client to time out to detect handshake failure, but that could
   impose a significant latency penalty on the client.  A server MAY
   proceed with the connection without verifying the token, though the
   server MUST NOT consider the client address validated.  If a server
   chooses not to proceed with the handshake, it SHOULD immediately
   close (Section 10.3) the connection with an INVALID_TOKEN error.
   Note that a server has not established any state for the connection
   at this point and so does not enter the closing period.

The problem with the "server MAY proceed" part is that the server's
transport parameters MUST include original_connection_id.  If ODCID
cannot be extracted from the invalid Retry token, then the server
cannot let this connection proceed.

Questions:

    1. The Retry token seems like the obvious place for the server
       to store ODCID.  Is there an implementation that does something
       else (state on server)?

    2. Should we spell out this caveat in the draft?  I.e. "server MAY
       proceed if it possesses ODCID."

Thanks,

  - Dmitri.

---

From: Christian Huitema <[email-masked]>
Date: Fri, 24 Jan 2020 18:18:06 -0800

>        proceed if it possesses ODCID."

What if the Initial packet carried a token from an expired NEW TOKEN frame?

-- Christian Huitema

---

From: Marten Seemann <[email-masked]>
Date: Sat, 25 Jan 2020 11:41:55 +0700

Then it's not a *Retry* token, then it's a NEW_TOKEN token.
It's a kind of subtle distinction (as I noted in
https://github.com/quicwg/base-drafts/pull/3107#discussion_r349859380).

---

From: Christian Huitema <[email-masked]>
Date: Fri, 24 Jan 2020 21:10:53 -0800

> https://github.com/quicwg/base-drafts/pull/3107#discussion_r349859380).

I understand that they are different. But look at the text in draft-25:

   close (Section 10.3 <https://tools.ietf.org/html/draft-ietf-quic-transport-25#section-10.3>) the connection with an INVALID_TOKEN error.

Invalid is invalid. An invalid token is something that the server cannot parse.
The server does not know whether the client got the token from a retry packet
or a new token frame, or just made it up with random bytes. 

-- Christian Huitema

---

From: Ian Swett <[email-masked]>
Date: Sat, 25 Jan 2020 16:36:51 -0500

This is a bit of an edge case, so spelling out what's required to proceed
seems reasonable to me.

---

From: Dmitri Tikhonov <[email-masked]>
Date: Sat, 25 Jan 2020 08:54:12 -0500

After considering this for a few days, I now think that the "server
MAY proceed" part should be removed from the draft.  This is because
it is impossible to get ODCID from an invalid Retry token.  There is
no other way reliably to associate an incoming packet with a previous
packet (if the server were willing to stored ODCIDs in memory
somewhere).

Specifying that the server MAY proceed only makes the issue more
confusing.

  - Dmitri.

---

From: Dmitri Tikhonov <[email-masked]>
Date: Tue, 28 Jan 2020 08:19:05 -0500

The server cannot pretend as if the Retry token weren't there,
because the client knows that it sent the Retry token and will
verify the subsequent Initial packet from the server according
to rules in Section 17.2.5 [1]:

   If the client received and processed a Retry packet, it MUST validate
   that the original_connection_id transport parameter is present and
   correct; otherwise, it MUST validate that the transport parameter is
   absent.  A client MUST treat a failed validation as a connection
   error of type TRANSPORT_PARAMETER_ERROR.

A conformant client will fail the connection.

  - Dmitri.

1. https://tools.ietf.org/html/draft-ietf-quic-transport-25#section-17.2.5

On Tue, Jan 28, 2020 at 07:39:48AM +0900, Kazuho Oku wrote:
> I am not sure if I agree with dropping the sentence.
> 
> IIRC, this sentence (i.e. "server MAY proceed with the connection without
> verifying the token, though the server MUST NOT consider the client address
> validated.") suggests that a server might proceed the handshake as if it
> did not receive the token when that token cannot be used.
> 
> I think that is not an unreasonable thing to do along with the other two
> stated option (i.e. discard packet, send CONNECTION_CLOSE without creating
> state).
> 
> Maybe changing "verifying the token" to "as if there was no token being
> attached" would clarify things?
> 
> 2020年1月27日(月) 22:15 Dmitri Tikhonov <[email-masked]>:
> 
> > > >    close (Section 10.3 <
> > https://tools.ietf.org/html/draft-ietf-quic-transport-25#section-10.3>)
> > the connection with an INVALID_TOKEN error.
> -- 
> Kazuho Oku

---

From: Kazuho Oku <[email-masked]>
Date: Wed, 29 Jan 2020 08:58:17 +0900

2020年1月28日(火) 15:45 Christian Huitema <[email-masked]>:

> But then, the client that responded to a retry will fail the connection if
> the ODCID parameter is not there. There are two alternative in the bad
> token scenario: good faith client with an obsolete new token, or client
> that received a bad faith retry. In the first case, there is no harm
> completing the connection. In the second case, the client will drop it so
> there is only a modest issue of temporary resource allocation.
>

While I agree that the two scenarios being described here are the
possibilities, I now tend to agree that the MAY can be dropped. Previously,
I missed the fact that ODCID has to be carried as a TP (as correctly
pointed out by Marten and Dmitri).

Regarding attack scenarios, I'd argue that it would be awkward for an
attacker to only send a spoofed packet to the server-side, when a malicious
middlebox can also send a packet to the client. I'd also argue that there
are many other ways of spoofing a client-sent Initial packet, and that
having a defense just for retry tokens does not change the picture.

Therefore, I think dropping the MAY is fine. I think a server can continue
to ignore the retry token when it assumes that it was spoofed, but my take
is that it's not worth writing it down.

> On 1/27/2020 8:35 PM, Marten Seemann wrote:
>
> The client knows if it used a NEW_TOKEN token. More specifically, the
> client knows if it received and responded to a Retry packet.
>
> On Tue, Jan 28, 2020 at 11:21 AM Christian Huitema <[email-masked]>
> wrote:
>
>>
>> On 1/27/2020 7:11 PM, Marten Seemann wrote:
>>
>> > That's actually what I implemented. If the token can be verified, accept
>> > the connection, treat it as validated. If it cannot be verified, check
>> > whether the token is necessary, e.g., would any incoming connection
>> > trigger retry. If it is necessary, drop the connection; if not
>> > necessary, continue as if no token was there.
>>
>> Doesn't that defeat the purpose of the original_connection_id transport
>> parameter? We introduced it to prevent middleboxes from performing a Retry.
>> With this logic, a middlebox could perform a Retry, and then corrupt the
>> token on the second Initial to trick the server into accepting the
>> connection.
>> Furthermore, client implementation will check for the
>> original_connection_id TP, and fail the connection if this TP is missing.
>>
>> The spec also has language that I'd interpret as forbidding this:
>> If the server sends a Retry packet, it MUST include the Destination
>> Connection ID field from the client's first Initial packet in the transport
>> parameter.
>>
>> How do you reconcile that requirement with the "NEW TOKEN" mechanism? If
>> the Initial packet carries a NEW TOKEN, it will be accepted and there won't
>> be any ODCID transport parameter.
>>
>> -- Christian Huitema
>>
>

-- 
Kazuho Oku