Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reconsider the use of FNV-1a for "cleartext" integrity #554

Closed
ekr opened this issue May 23, 2017 · 7 comments
Closed

Reconsider the use of FNV-1a for "cleartext" integrity #554

ekr opened this issue May 23, 2017 · 7 comments
Labels
-transport design An issue that affects the design of the protocol; resolution requires consensus. has-consensus An issue that the Chairs have determined has consensus, by canvassing the mailing list.

Comments

@ekr
Copy link
Collaborator

ekr commented May 23, 2017

We had consensus to use FNV-1a to provide integrity for packets in non-adversarial settings. However as observed in #522, we still have to potentially worry about packet injection from off-path attackers. A natural design is to have the pre-handshake integrity check be somehow keyed by information in the ClientHello (e.g., the conn id). In this case, we would probably want to use something more like HMAC, though it's not necessarily required.

@martinthomson martinthomson added design An issue that affects the design of the protocol; resolution requires consensus. -transport labels May 24, 2017
@martinthomson
Copy link
Member

Is this just a more complicated way of providing proof of receipt?

@ekr
Copy link
Collaborator Author

ekr commented May 24, 2017

I don't think so.... It's rather a way of avoiding injection of bogus handshake packets by off-path attackers.

@ianswett
Copy link
Contributor

ianswett commented Jun 8, 2017

While we're reconsidering this, @martinduke and I were talking again about the idea of encrypting using AES-GCM with a version specific key. That re-convinced me it was a good idea.

  1. AES-GCM hardware offload is a common feature, and being able to use it as much as possible is nice.
  2. Forces middleboxes that want to do TLS inspection to stay up to date with the latest versions, otherwise they won't be able in inspect SNI. This makes future changes to TLS less likely to ossify, since middleboxes that aren't frequently updated won't be able to see the new version of TLS.

Given recent deployment issues with TLS 1.3, I think point 2 is more valuable than I previously believed.

@martinduke
Copy link
Contributor

martinduke commented Jun 8, 2017 via email

@mikkelfj
Copy link
Contributor

mikkelfj commented Jun 9, 2017

While not against the idea,

It would greatly simplify the permutations if packets were always
encrypted, and it was just a matter of switching from an insecure key to a
secure one.

does not hold when encryption is not necssarily AES-GCM after handshake or in a new version. Further, some low-end IoT devices do not have AES accelleration, and will not have, and may want to use ChaCha20 instead. Such devices could use a generic implementation since it is only for handshake, but it does kill the simplicity argument.

I have previously in a private mail to some authors suggested signing the cleartext headers using a CMAC algorithm with a version specific key. This is much simpler than AES-GCM and better suited for non-version specific implementation. CMAC will not encrypt, but AES can still be used for that, if the concern is ossification.

Regarding ossification - isn't there a risk that middleboxes will only work if they understand the protocol, and thus will reject newer QUIC versions with a key they don't have? This could make the problem worse. I'm just speculating as I have no experience with these issues.

EDIT: since a significant part of cleartext packets has to do with version negotiation, I also wonder how well a version specific key. Regarding my earlier CMAC propososal, the motivation was to filter out packets that are not QUIC before expending semi-expensive crypto hash algorithms - but others argued that crypto is generally fast enough to be a non-concern (and I would add that IoT devices can also live with some crypto delay / power drain for handshake).

@mikkelfj
Copy link
Contributor

mikkelfj commented Jun 9, 2017

For reference here is the CMAC proposal from March 15 I sent:

I have an additional concern - it can be expensive to verify maliciously corrupted packets.

It has been raised before by others:

https://www.blackhat.com/docs/us-16/materials/us-16-Pearce-HTTP2-&-QUIC-Teaching-Good-Protocols-To-Do-Bad-Things.pdf
Slide header: ABUSING - NEW PRTOCOLS

QUIC ought to have a fast verification of origin in each header. If this is not done, the receiver is forced to do a full packet authentication, which for AES-GMAC would at least require a GMAC computation over the entire packet which could be anything from a few bytes to jumbo frames.

An attacker can inject valid connection ids with invalid authentication tags at a high rate. The attacker does not even need to spoof the source address since QUIC supports connection migration.

Fixed length AES-CMAC on the header fields would solve this problem at the cost of a longer header and more processing in general, which the implementation may opt to ignore as long as most packages authenticate in the original sense.

https://tools.ietf.org/html/rfc4493
https://tools.ietf.org/html/rfc4494

A simpler approach, given that a short fixed length header would be the following (which I have not studied in detail, so this is just a starting point):

Concatenate 8 byte connection id, 8 byte packet number and xor the type byte msb packet number. This yields a 16 byte block that can be encrypted with a derived traffic key. Since we have a derived key and a fixed size single block, we don’t really need the full CMAC machinery.

The resulting encrypted value can be truncated to two bytes which gives an attack 1/2^16 chance of guessing the tag. This ought to make it efficient against DoS attacks. Furthermore it is fairly straightforward to implement in hardware if the traffic key is available.

For unencrypted packets the header tag should be zero, or absent if detectable via the type byte.

One problem with this approach is that it is trivial to replay an existing packet and either just keep repeating a valid packet, or to intentionally corrupt the long version tag. But an attacker would have to be lucky to get a bad packet ahead of a good packet so the receiver just needs to drop duplicate packets as it would have to anyway. If the attacker merely replays packets, it does not matter which one gets ahead.

Another problem with this approach is the requirement of using AES in the protocol header, but it is likely much faster than HMAC-SHA256 and there is virtually no platform that doesn’t have access to AES-128, and it is still entirely optional to perform the check.

With this approach, randomisation of packet sequence numbers is also likely not needed. It is a return to the diversification nonce, just in a different form. It does of course cost two bytes, but that is probably worthwhile.

This could also relate to public reset proof, but I’m not sure how the crypto context is valid at that point. Also, it would only require 2^16 attempts to force a reset.

Follow up mail:

The approach discussed earlier can be extended to unauthenticated headers:

Each protocol version publishes a fixed AES-128 key (several versions may use the same key) and protects the header with a two byte tag as for protected packets. This serves the dual purpose of providing protocol magic bytes and a header hash.

This helps separate traffic from other UDP protocols and to an extent middle box manipulation of unprotected headers.

It may possibly also help mitigate DoS attacks on the TLS handshake. I have not studied what the handshake does internally, but it can prevent starting the TLS machinery in the first place.

There is a risk that middle boxes hardcodes the key, making the protocol version difficult to change. For this reason the key should not necessarily be tied to a specific protocol version.

Proprietary deployments such as in-house database connections may configure a random fixed custom key for the header tag. This is not intended to be strong encryption but it prevents random external communication (early rejection before TLS) and the key cannot be trivially guessed from just the 16 tag bits. It is not sufficient to provide non-idempotent 0-RTT transactions.

I’m sure you have already considered something along the following lines, but for completeness:

I have not yet studied 0-RTT in detail, but it appears to me that a simple 64-bit counter along with the stored crypto context would be enough to prevent some replay attacks. In this case the public tag key could be replaced with a CMAC that includes a 64-bit counter that is stored with the crypto context at client and server. Because of the counter field as extra information in addition to the header, a full CMAC and not just a single AES encryption may be required, or the counter could be placed in the high part of the implicit 64-bit packet number. Because the tag is only two bytes this is still insufficient to handle idempotent 0-RTT transactions but it allows early rejection (before TLS engine) of replays. The tag could be extended to more than two bytes, but it is probably better to also include the counter in the full 0-RTT handshake protocol and include it in some tag there.

The counter must be larger than any previous connection attempt by at most a small amount (small enough to retry different counters). The counter is not (necessarily) included in the header other than in hashed form because it would leak information about how many connection attempts have been made, and it is not strictly needed. The counter adds complexity to a server clusters because the counter must be synchronized, but so must the crypto context. Allowing some variation in next counter value should handle both sync delays and interrupted connection attempts. For unstable connections and delayed server sync the fallback is then 1-RTT. If the counter is not explicit in the header, a DoS attack might force many CMAC counter increment attempts, which is why it must be a small increment.

@ianswett
Copy link
Contributor

To be replaced with #693

@martinthomson martinthomson removed this from Crypto in QUIC Aug 30, 2017
@mnot mnot added the has-consensus An issue that the Chairs have determined has consensus, by canvassing the mailing list. label Mar 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
-transport design An issue that affects the design of the protocol; resolution requires consensus. has-consensus An issue that the Chairs have determined has consensus, by canvassing the mailing list.
Projects
None yet
Development

No branches or pull requests

6 participants