Subject: [quicwg/base-drafts] Stream Flow Control on Stream 0 (#1252)

From: martinduke <[email-masked]>
Date: Tue, 27 Mar 2018 20:25:30 +0000 (UTC)

Perhaps all this will be obviated by ekr, etc. work on the "Crypto Stream" PR, but here goes anyway...

It bothers me that Stream 0 is not subject to stream flow control limits throughout the handshake. Subjecting it to *connection* flow control could absolutely have unintentional consequences, but explicit flow control for the crypto handshake is no harder than what we manage with TCP every day.

One can imagine a fairly trivial attack where a client, after getting a request for a certificate, delivers stream 0 offsets N+10 to N+<arbitrarily large number>, and the server is required to store it all. It is certainly possible to write some "don't accept stupid stuff" code in the server, but it would be way better to allow this control within the protocol without sketchy heuristics.

Obviously, the Initial packet would still not be subject to these limits.

I would like this tweak to appear in the new proposal. If it is worthwhile to submit a PR against the current text, I am happy to do so.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252

---

From: Mike Bishop <[email-masked]>
Date: Tue, 27 Mar 2018 14:01:45 -0700

The reason we don't employ stream-level flow control until the handshake completes is because you can't authenticate the flow control messages you received.  This means you have to land in one of three camps:

1. **Can't grant additional flow control credit until handshake finishes.**  But then if the handshake *needs* more flow control, it deadlocks.
2. **1-RTT flow control messages overwrite 0-RTT flow control messages.**  But if you never send a flow control update under 1-RTT keys, the attacker-controlled value stands.
3.  **Normalize the stream flow control after the handshake completes.**  Closing the stream is a special case of this.

We wound up with (1), and the flow control exemption to avert the deadlock.  It's not the only reasonable solution, but it's a more nuanced problem than it first appears.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-376674096

---

From: martinduke <[email-masked]>
Date: Tue, 27 Mar 2018 14:15:03 -0700

@MikeBishop Thanks for the explanation.

When we look at possible harm to a fully compliant implementation, I don't think the current text holds up well: as described above, there is no limit to the data a server must store if it requests a certificate.

The attacker you suggest is annoying, but it's a nuisance rather than fatal:
1) If the attacker tries to lower the amount of credit the client has, the client will ignore it.
2) If the attacker tries to increase the credit, it's still limited by data the client has to send. Moreover, the server will definitely drop the out of bounds data. (In yet another instance of the "stream 0 is special" list, we'd better not set FLOW_CONTROL_ERROR in this case.)

But yes, I see the need for some correction mechanism once keys are established.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-376677878

---

From: Nick Banks <[email-masked]>
Date: Tue, 27 Mar 2018 21:28:36 +0000 (UTC)

I think I agree with @martinduke here. Why not allow the flow control frames to be sent during the handshake, and then recommend (require?) that each side send an updated flow control frame in 1-RTT after the handshake completes? Then, if things don't add up, we either self correct or kill the connection.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-376681685

---

From: Christian Huitema <[email-masked]>
Date: Thu, 29 Mar 2018 21:04:56 +0000 (UTC)

Be careful what you ask for, because ill-behave clients could use flow controls and force servers to stall, leading to a DOS attack.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377372051

---

From: martinduke <[email-masked]>
Date: Thu, 29 Mar 2018 14:48:28 -0700

@huitema That's true, but this is a general problem with flow control no matter what we do with the handshake. In other words, a production server implementation will have to kill stalled connections regardless, stream 0 or not. The current stream 0 language introduces a new attack that is not possible on other streams.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377382897

---

From: Christian Huitema <[email-masked]>
Date: Thu, 29 Mar 2018 15:23:21 -0700

@martinduke sure, but during the handshake the bulk of stream zero data is sent from server to client. The Client Initial can carry at most 1200 bytes, and the second client flight is typically only a few hundred bytes. The only purpose of flow control would be to limit the amount of data sent in the server first flight -- and we already have text about only sending three packets before requesting a path check. I really wonder where the attack might be.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377390706

---

From: martinduke <[email-masked]>
Date: Thu, 29 Mar 2018 15:30:41 -0700

As I said, the issue only applies that servers that require client certificates. I don't see how the 3-packet limit is relevant to that attack at all.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377392288

---

From: Christian Huitema <[email-masked]>
Date: Thu, 29 Mar 2018 22:38:29 +0000 (UTC)

So the attack happens when a server requires a client cert, and the client sends back something humongous. Not sure that flow control is the right tool there. Feels more like "don't send me a certificate larger than X". 

Also, nasty clients could use certificate compression in interesting ways, despite the flow control limit.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377393854

---

From: martinduke <[email-masked]>
Date: Thu, 29 Mar 2018 15:44:33 -0700

I assume that TLS has some defenses against garbage cert chains - or more precisely, this is not a problem for QUIC. The specific issue is when offsets are deliberately missing -- then there's just a bunch of junk sitting in the stream 0 receive buffer.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377394978

---

From: Subodh Iyengar <[email-masked]>
Date: Fri, 30 Mar 2018 08:30:19 +0000 (UTC)

I think crypto stream is actually special in this case and doesn’t need flow control. Flow control only makes sense in 2 cases

1. The transport is going to buffer something and the application will choose not to read it
2. The sender can make a different decision based on the flow control

Tls messages are sent in records which are 16kb large. As soo. As the transport receives a message on the crypto stream it is going to read it out with the highest priority and supply it to the tls layer. This means that buffering is done at the tls later and not at the transport later. Also the tls layer only buffers 16k at a time and not all the data. Another layer of buffering happens at the asn parser for certificates. If this layer gets too big tls implementations can just close the connection unilaterally. The tls layer has a much better idea of how much is buffered than the transport layer so the transport layer should not do flow control.

Secondly what is the receiver going to do if it has 1 cert but it cannot send it? Will it choose a different smaller cert dynamically? This would be bad because it’s not based on authenticated information. I would rate this as analogous choosing the cert based on sigalgs if the sigalgs were not authenticated. So if the sender cannot make a different choice dynamically, the user of the api has to analyze the data and make a config change later.

I’m inclined to say that each side maintains their own private limit which is reasonably large and they can change it, and it you’re sending uber large Certs, don’t do that.

We could possibly have the tls layer incidicate some informational aspect of what it’s buffereing limit is to the peer but not need to track it for sending but only for later analysis 

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377477355

---

From: Subodh Iyengar <[email-masked]>
Date: Fri, 30 Mar 2018 01:57:15 -0700

I think for the garbage sitting in stream 0 buffers we can also have a unilateral limit that each side enforces independently 

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377482069

---

From: MikkelFJ <[email-masked]>
Date: Fri, 30 Mar 2018 09:02:04 +0000 (UTC)

That makes a lot of sense, including the previously longer mail on TLS
doing the policing.

How does that relate to crypto after handshake, notably client
authentication?

Mikkel

On 30 March 2018 at 10.57.14, Subodh Iyengar ([email-masked])
wrote:

I think for the garbage sitting in stream 0 buffers we can also have a
unilateral limit that each side enforces independently

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377482069>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AALzN4hM64msYWP_j1dND0KqRNmN-diZks5tjfNqgaJpZM4S9lIq>
.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377482893

---

From: Subodh Iyengar <[email-masked]>
Date: Fri, 30 Mar 2018 13:20:00 +0000 (UTC)

Post handshake client auth is not allowed with quic iirc. As for other data after the handshake on the handshake stream that is new session ticket has only variable length data which is limited by 2^16 bytes which is small enough to be a constant overhead

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377522570

---

From: martinduke <[email-masked]>
Date: Fri, 30 Mar 2018 15:38:28 +0000 (UTC)

@siyengar I think you missed an important case for flow control: to limit how much has to be stored in the receive buffer until holes in the sequence number space are filled. Indeed, that's the entire point of this issue. In most implementations, buffering at the TLS layer is irrelevant if these sequence number gaps exist.

If a client cert is too large, then a well-behaved server will not provide the credits to deliver the entire thing until the first chunk arrives in its entirety so that it can be assembled and passed up to TLS. This is how things work in TCP, and I don't see that it's a hard problem modulo @MikeBishop's point about spoofed MAX_STREAM_DATA frames.

What is the point of maintaining private, independent limits? This is just flow control without explicit signaling! It can lead to many difficult questions about whether or not to ack such a packet, which implies that the data was processed when it wasn't.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377551935

---

From: Subodh Iyengar <[email-masked]>
Date: Fri, 30 Mar 2018 21:25:53 +0000 (UTC)

I addressed that with my second comment. I think that limits can be independent of each other even in that case. 

I don’t understand what acking a packet has  to do with it. I was suggesting closing the connection with an error code :) if your limit is exceeded

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377626880

---

From: martinduke <[email-masked]>
Date: Fri, 30 Mar 2018 21:58:35 +0000 (UTC)

OK, the independent limit is a hidden tripwire to tear down the connection. Why is that superior to explicitly using the existing flow control message to tell the client not to exceed that?

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377632773

---

From: MikkelFJ <[email-masked]>
Date: Fri, 30 Mar 2018 15:12:43 -0700

> OK, the independent limit is a hidden tripwire to tear down the connection. Why is that superior to explicitly using the existing flow control message to tell the client not to exceed that?

Stream 0 is not entirely trusted and you can a lot abuse before anything has been negotiated.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377635167

---

From: martinduke <[email-masked]>
Date: Fri, 30 Mar 2018 15:17:48 -0700

We're going in circles. Please see the third message in this thread.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377636018

---

From: Subodh Iyengar <[email-masked]>
Date: Sat, 31 Mar 2018 11:31:13 +0000 (UTC)

Sorry i should have probably been more clear in my last comment. My point was that all these cases are related. A receiver uses flow control to limit the amount of buffering it does without processing the bytes. Let’s assume we did have a way to do dynamic flow control in cleartext. I still believe that there is no use of doing so. In the absence of an attacker there are 2 cases:

1. Reciever can buffer more bytes than sender has. No problem, flow control doesn’t come in use here
2. Reciever can buffer less bytes at once than the sender has to send. In this case normally a reciever would use flow control. However in the case of a large cert the receiver is baisclally buffering these bytes just in the tls buffers. At this point flow control was really facetious. So a rational receiver would set the limit to be the limit that the tls layer would be willing to buffer anyway. Ok now that the limit is the tls layers limit, if this limit is less than what the sender wants to send, the sender is screwed anyway regardless of whether there is flow control. Also since the limit is the size of the tls buffer, that is the same limit the quic stream later should enforce for out of order data. At this point there is no use of explicitly signaled flow control

What is really the problem here is that there is really a hard limit that the reciever has that if the sender cannot satisfy there is no point of negotiation. So we have to assume that people set reasonable limits and call it a day 

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377686503

---

From: Praveen Balasubramanian <[email-masked]>
Date: Sun, 01 Apr 2018 01:24:49 +0000 (UTC)

If there are reasonable limits that are not communicated or negotiated, should there be an error code to reset the connection if that limit is exceeded? 

I see that the recovery draft talks about a dedicated alarm and retransmissions for handshake data but doesn't explicitly say anything about congestion control for stream 0. Do we need a separate issue for whether congestion control should be applied to stream 0?

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377735611

---

From: Praveen Balasubramanian <[email-masked]>
Date: Sat, 31 Mar 2018 18:28:18 -0700

I suppose it is implicit that congestion control is applied to all streams since it is at the packet level, but we have special cased handshake data.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1252#issuecomment-377735778