Sunday, April 4, 2021

Quic: the Elephant in the Room

Status Check for African Elephants | NRDC
Elephants should be here, not in rooms

[foreword:  I revised this several times expanding my thoughts and worked on getting packet sizes and counts correct, but it's quite possible I've made some mistakes in the process.]

I was recently thinking about Quic, the combined TLS and Transport protocol that Google initially designed to streamline session start up and a wish list of other improvements to better target web traffic needs. The motivation is mainly latency and the number of round trips needed to start flowing the underlying HTTP traffic. While Quic certainly does that and is an improvement with the strict layering with TCP, looking at this from an outside perspective (I am no TLS expert), the biggest source of latency at startup seems to be sending the server certificates themselves. I looked at my certs and the full chain pem file (ie, my cert plus the signer's cert) are about 3500 bytes. I tried gzip'ing them and it made some difference, but was still about 2500 bytes all said and done, but TLS doesn't seem to be doing that. So that's a minimum of three MTU sized packets just for the credentials and one MTUish sized packet for the ClientHello. While the cert packets are sent in parallel given the congestion window like TCP, they are still MTU sized packets which have the latency that Google was trying to get rid of. One curious thing I noticed is that Wireshark seemingly said that it was using IP fragmentation which if true is really a Bad Thing. I sure hope that Wireshark got that wrong.

If I understand Quic correctly, basically they got rid of the the TCP handshake and used the TLS handshake instead since it's a 3 way handshake too. So the flow goes sort of like this:

 

  • DNS A/AAA Lookup ->
  • DNS Response A/AAAA <-
  • ClientHello+Padding ->
  • ServerHello+QuicHandshake1 (cert) <-
  • QuicHandshake2 (cert cont) <-
  • QuicHandshake3 (cert cont) <-
  • QuickHandshake (finish) ->

So in all the server is sending ~3 MTU sized packets. This is on the assumption they are sending pem which might not be a good assumption as they could be sending the straight binary X.509, but from the looks of it on Wireshark it looks like they're just sending PEM. I'm assuming that the ClientHello  is small, but I read that there are issues with reflection attacks so they are relatively large. Assuming that I read that right it's about 1200 bytes for the client and server Hello, so all told 4 ~MTU sized packets and small client finished handshake packet. So in bytes, we have about 1300+1500+1500+1000+100 which is ~5400 bytes.

Getting Rid of Certificates using DNS

What occurs to me is that if they weren't using certificates it could be much more compact. The rule for the reflection attack is that the server should send no more than 3 times the ClientHello packet size. Suppose instead of using certificates we used something like DANE (RFC 6698) or a DKIM (RFC 4871) selector like method:

  • DNS A/AAAA Lookup ->
  • [ DNS TLSA Lookup -> ]
  • DNS A/AAAA Response <-
  • ClientHello+Padding ->
  • ServerHello+QuicHandshake <-
  • [ DNS TLSA Response <- ]
  • QuicHandshake (finish) ->
The server QuicHandshake would be relatively small depending on whether you fetch the public key from the DNS or just query DNS as to whether, say, a fingerprint of a sent public key is valid (DANE seems to be doing the latter). In either case, the size of the QuicHandshake is going to be quite a bit less than an MTU, say 600 bytes. That means that the ClientHello only needs to be about 200 bytes or so, so it is a medium sized packet. Thus we've reduced the sizes of the packets considerably. That's 3 small packets and two medium ones. In bytes it's like 200+600+100+300+100 which is about 1100 bytes about 5x smaller.  
 
But wait, there's more: DNS is cachable so it's pretty likely that the DNS response is going to be sitting in cache so it becomes 3 smallish messages and ~900 bytes instead which is about 7x smaller and 2 messages less. It also doesn't have any problem with IP fragmentation if that's really what's going on. Plus we're back to the traditional the 3 packet handshake as with TCP.  Note that DNSSec requires additional lookups for DNSKEY and DS RR's but many of these will end up in caches, especially for high traffic sites.
 
Using DNS obviously would in this case require DNSSec to fully reproduce the security properties of certificates but that shouldn't be an impediment. As with the original Quic from Google, Google owns both browsers and servers so it controls whether they come to agreement or not. All they have to do is sign their DNS repository (which I assume they already do) and the browser needs to make certain that the DNS response is signed properly. All of this can happen in user space that is completely under their control.

Update: I moved the DNS TLSA lookup to be speculative after the A/AAAA record lookup if it's not in cache. The client could keep track of the domains that have produced TLSA records in the past as a means to cut down useless speculative lookups. A better solution would be to have the TLSA record "stapled" to the A/AAAA lookup, but I'm not sure what the rules for such things are, and of course it would require buy in from the DNS server to add them to the Additional RRset.

DNS Implications

Using DNS as a trust root is a much more natural way to think about authentication: domains are what we are used to either trusting or not. Certificates created an alternative trust anchor and frankly that trust anchor is pretty self-serving for a whole lot of certificate vendors. It would obviate the need for that side channel trust anchor and get it on the authority of the domain itself directly. Gone would be the need to constantly renew certificates with all of the hassle. Gone would be the need to pay for them. Gone would be the issue of having dozens of certificate roots. Gone would be the risk of one of those roots being compromised. Gone would be a business model that was predicated on 40 year old assumptions of the need for offline verification which is obviously not needed for an online transport layer protocol. 

Another implication is wildcards. Certificates have the ability to have wildcards in the name space, so that foo.example.com and bar.example.com can have one certificate with *.example.com. DNS has wildcards too, but whether they would meet the security properties needed is very questionable as I'm pretty sure that there is a lot of agreement that DNS wildcards are messed up to begin with. If they don't, you'd have to enumerate each subdomain's DANE records. I'm willing to bet that DANE addresses this, but haven't seen it specifically in my skim of it.

Another implication is that a lot of clients rely on upstream resolvers which is a thorny issue when authentication is involve. However, my experience is that browsers either implement their own stub resolver, or rely on a OS stub resolver. Given ecommerce, etc, my feeling is that trying to eek out some sort of CPU performance benefit is generally a bad tradeoff and that browsers can and should actually authenticate each transaction before storing it in a local cache. RSA/ECDSA verifies are extremely cheap these days, and besides browsers are already doing those verifies for certificates.

TLS Implications

 
I am by no means a TLS expert and can barely play one on TV, but my understanding is that TLS allows for naked public keys these days. Update: this is specified in RFC 7250 and uses X.509 syntax, but strips everything out by the public key. I'm not sure how TLS deals with validating the raw public key, but I assume that it just hands it up to the next layer and says "it validates, whether you trust it is now your problem". That takes the DNS/DANE exchange completely out of the hands of TLS so implementers wouldn't need to get buy in from TLS library maintainers.  
 

An Alternative for Certificates

While certificates require 3 packets to transmit, it is not inevitable that they must be sent each time a session is started. A client could in principle send the fingerprint(s) of certificates that it has cached for the domain in the ClientHello and the ServerHello could then reply with the chosen certificate fingerprint if it has possession of its key. That too would cut the exchange down to 3 packets instead of the 5. The downside is that it would require buy in from the TLS community to implement the new protocol extension. Additionally, the ClientHello would still be required to be an MTU'ish sized packet since the client wouldn't necessarily know whether the server supports that extension or not.

Conclusion


I've stressed throughout this that a Google-like company could take this into their own hands and just implement it without buy in from anybody. That was what made Quic possible in the first place since anything else than that is beating up against  an ossified and sclerotic industry. Indeed the Certificate Industrial Complex would completely lose their shit as their gravy train is shut down. Given DANE and DKIM, the use of DNS to authorize public keys for use elsewhere is well understood and should be completely safe given DNSSec, and arguably safer given that there are far fewer middle men CA's involved to screw up. 
 
A real life implementation would go a long way to proving how much latency it would cut out because my numbers here are all back of the envelope. It remains to be seen what the actual improvement is. But if it did nothing more than break the back of CA's, that would be an improvement in and of itself. Admittedly, this only changes the startup cost, not the per packet cost which might contribute to some of the gains that Quic sees. Since Quic allows longer lived connections and multiplexing of requests to deal with head of line blocking, it's not clear whether the gains will be significant or not. The business side implications, on the other hand, are clearly significant, though it has to be said that x.509 would need to be supported for a good long time.