Subject: [quicwg/base-drafts] Version Negotiation DoS (#522)

From: janaiyengar <[email-masked]>
Date: Thu, 11 May 2017 22:02:39 -0700

A Version Negotiation (VN) packet now contains a server-chosen connection ID and echoes the client's packet number, for a client to be able to validate that the server did see the client's packet. 

There are no rules around what a client should do on receiving VN packets with random IDs and packet numbers that do not match. If the client just drops them on the floor, an attacker can send a bunch of VN packets with an arbitrary connection ID and with random packet numbers, hoping to kill the client's ongoing handshake.

Is 4 bytes of entropy enough to prevent this DoS?

-- 
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/522

---

From: Marten Seemann <[email-masked]>
Date: Thu, 11 May 2017 22:10:45 -0700

An attacker would also have to guess the right port number, which should be close to 2 bytes of entropy in itself.

-- 
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/522#issuecomment-300985312

---

From: janaiyengar <[email-masked]>
Date: Thu, 11 May 2017 22:18:31 -0700

Yeah, good point. But OSes are bad at handing out port numbers randomly -- I could be wrong, but I believe OSes hand these out sequentially to sockets. There may be far fewer than 2 bytes of entropy there.

-- 
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/522#issuecomment-300986138

---

From: Martin Thomson <[email-masked]>
Date: Thu, 11 May 2017 22:32:16 -0700

I said this over [here](https://github.com/quicwg/base-drafts/issues/161#issuecomment-300986257), but I'll add it here for the benefit of this thread.

> I don't assume that Version Negotiation gives any indication of consent to receive, though the echo of a 31-bit value might suffice (TCP is 32). It doesn't need to really, because the client is only going to send the same packet that it would have sent without seeing any packets from the server.

But I don't think that addresses the issue here.  The issue here is that a client might want to validate a Version Negotiation packet.  I separately opened #523 on that point, but this seems like it is asking the question about whether we have enough entropy to ensure that the Version Negotiation packet is OK.

BTW, it's only 31-bits of entropy.  Port number might be close to zero entropy, though in practice it's a fair bit better than that (maybe as much as 14 bits) and we might assume that the IP address is known, so zero entropy.  If the benchmark is TCP, then we're 1 bit worse off.  On the other hand, if the benchmark is DNS, things don't look too bad.

Modern operating systems generate ephemeral addresses randomly.  But it's usually from a small-ish slice of ports.  Test that if you like:

```
node -e 'var s = require("dgram").createSocket("udp6");s.bind(function(){console.log(s.address().port);s.close();});'
```

-- 
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/522#issuecomment-300987705

---

From: MikkelFJ <[email-masked]>
Date: Thu, 11 May 2017 22:49:57 -0700

... which goes back to my request to linking client chosen id to the server chosen id - in this case it would reduce attacks to those on path.

-- 
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/522#issuecomment-300989735

---

From: Martin Thomson <[email-masked]>
Date: Thu, 11 May 2017 23:09:20 -0700

Oh, so that I don't forget, we only use 31-bits because of the need to increment this number.  If we randomize for every Client Initial packet, which seems perfectly doable now, then we can use the entire 32-bit space.  That's all assuming that 32 is enough but 31 isn'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/522#issuecomment-300992148

---

From: Martin Thomson <[email-masked]>
Date: Thu, 11 May 2017 23:11:01 -0700

I just tested Windows 10 and it *doesn't* randomize the source port for ephemeral UDP.  So no entropy there :(  Back down to 31.

-- 
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/522#issuecomment-300992397

---

From: ekr <[email-masked]>
Date: Sat, 13 May 2017 11:29:03 -0700

This seems like a more generic issue in that it's possible to create a DOS attack essentially by sending any packet that appears to be from the server to the client that passes the QUIC packet checks but is bogus at an upper layer. For instance, one could send an SH that had a cipher suite 
that the client did not offer.

Implicitly, this issue assumes that we want (or will live with)

- On-path attackers to be able to terminate a connection during the handshake phase
- Off-path attackers not to be able to do so.

It seems like it would be easiest to address this problem generically rather than
as a side effect of other designs. We've already agreed that we are going to have
an integrity check on packets, so it seems like the easiest thing to do would be
to have it be keyed with the conn-id.

Specifically:
Client initial packets are either HMAC(0, <packet>) or HMAC(conn_id, <packet>), depending on what's convenient.
Server initial packets are HMAC(conn_id, packet).

This will prevent injection attacks by any attacker who hasn't seen the client initial packet, this fixing both this issue and others.

-- 
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/522#issuecomment-301266057

---

From: MikkelFJ <[email-masked]>
Date: Sat, 13 May 2017 12:13:02 -0700

This almost makes sense to me except

- the HMAC cannot trivially be version dependent then.
- the HMAC key is public, so you don't gain much compared to returning the initial connection id as I suggested earlier.

-- 
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/522#issuecomment-301268777

---

From: ekr <[email-masked]>
Date: Sat, 13 May 2017 12:15:36 -0700

> the HMAC cannot trivially be version dependent then.

I don't understand this. The HMAC is just replacing FNV-1a, so we'll pick an algorithm and stick with it.

 > the HMAC key is public, so you don't gain much compared to returning the initial connection id as I suggested earlier.

What you gain is having something that's generic rather than something that requires specific intervention and analysis for each case.

-- 
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/522#issuecomment-301268921

---

From: MikkelFJ <[email-masked]>
Date: Sat, 13 May 2017 12:30:11 -0700

>> I don't understand this. The HMAC is just replacing FNV-1a, so we'll pick an algorithm and stick with it.

Yes, exactly - if 22 years from now, HMAC-SHA256 is trivially broken, you cannot replace it. FNV-1a does not attempt to be cryptographically strong, so it cannot break in the same way, but offer fewer guarantees in return. Note that I am not against using HMAC because it is a stronger defence againts obnoxious middleware, and since unencrypted payload is not bottleneck, there isn't much gain in choosing a cheaper solution. However, FNV-1a is simpler to specify as universally true which is probably why it was chosen.

> What you gain is having something that's generic rather than something that requires specific intervention and analysis for each case.

Well, yes I do not disagree, I am just pointing out the the generic solution could also be just returning  the initial Connection ID because the HMAC in this setup provides not stronger guarantees as far as I can tell. The only guarantee it provides is that that payload has not been tampered with, but that is for the handshake to decide, and the handshake goes to great lengths to support extensible cryptography (for better or worse). Alas, I wouldn't mind a much simpler QUIC version that just decided on CURVE25519 and be done with it, then change version if needed. And this is indeed possible to do. But for a generic version 1 of QUIC, you need to consider all this crypto replacability, I'm afraid.

But, even before you wrote this, I was also thinking why not use a HMAC instead of FNV-1a, so I largely follow what you say.

-- 
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/522#issuecomment-301269853

---

From: ekr <[email-masked]>
Date: Sat, 13 May 2017 12:56:16 -0700

On Sat, May 13, 2017 at 12:30 PM, MikkelFJ <[email-masked]> wrote:

> Yes, exactly - if 22 years from now, HMAC-SHA256 is trivially broken, you
> cannot replace it. FNV-1a does not attempt to be cryptographically strong,
> so it cannot break in the same way, but offer fewer guarantees in return.
> Note that I am not against using HMAC because it is a stronger defence
> againts obnoxious middleware, and since unencrypted payload is not
> bottleneck, there isn't much gain in choosing a cheaper solution. However,
> FNV-1a is simpler to specify as universally true which is probably why it
> was chosen.
>
Well, we chose FNV-1a when we weren't attempting to have something keyed.

WRT to the question of "trivially broken", I suppose that that's possible,
yes,
but then we'll basically be back to the situation we are in now. And you
would
need a very strong attack, one in which you had the ability to forge a MAC
with an unknown key and where you didn't get any known plaintext/ciphertext
pairs. To the best of my knowledge, we're nowhere near this with HMAC,
even with MD5.

It's always risky to analyze on the fly, but it seems like in order to have
this,
you would need to find some set of inputs to the compression function
which would spit out the same output with high probability no matter what
the current state of the compression function is.

However, if this is really something people are worried about then the
trivial
solution is to have an "integrity check" with consists of

conn_ID || FNV-1a(conn_id || packet)

This guarantees that no possible attack on the integrity function can result
in the attacker generating a packet which will be acceptable without knowing
the conn_id.

the HMAC key is public, so you don't gain much compared to returning the
> initial connection id as I suggested earlier.
> What you gain is having something that's generic rather than something
> that requires specific intervention and analysis for each case.
>
> Well, yes I do not disagree, I am just pointing out the the generic
> solution could also be just returning the initial Connection ID because the
> HMAC in this setup provides not stronger guarantees as far as I can tell.
> The only guarantee it provides is that that payload has not been tampered
> with, but that is for the handshake to decide, and the handshake goes to
> great lengths to support extensible cryptography (for better or worse).
>
I don't think it is actually for the handshake to decide. The handshake
responds
to totally bogus data by reporting handshake failure, not by rejecting the
questionable
packet. For this reason it's more desirable to have the integrity check at
the layer
below.

WRT to returning the initial connection ID, in order to achieve the right
properties
you need to return it in *every* server packet before crypto is
established, or you
have potential problems. This seems goofy when you can just shove it in the
integrity check and have packet rejection at the lower layer.

-- 
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/522#issuecomment-301271162

---

From: MikkelFJ <[email-masked]>
Date: Sat, 13 May 2017 13:46:57 -0700

I'm just going to throw this CLMUL hash into the equation, just for reference: 

Faster 64-bit universal hashing using carry-less multiplications
https://arxiv.org/pdf/1503.03465.pdf

-- 
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/522#issuecomment-301273766