libp2p / go-libp2p

libp2p implementation in Go
MIT License
5.98k stars 1.05k forks source link

quic: private network support #1432

Open Stebalien opened 5 years ago

Stebalien commented 5 years ago

Currently, the QUIC transport is incompatible with private networks as it doesn't use the pre-shared key in any way.

We have a couple of options:

  1. Implement the proposed TLS1.3 PSK extension: https://tools.ietf.org/html/draft-ietf-tls-tls13-cert-with-extern-psk-00. This allows mixing the pre-shared key with the result from the handshake.
  2. Add the ability to layer on some kind of trivial packet-based encryption protocol. We can use the same crypto as the current private network library as long as we use a new nonce per packet.
marten-seemann commented 5 years ago

Implement the proposed TLS1.3 PSK extension: https://tools.ietf.org/html/draft-ietf-tls-tls13-cert-with-extern-psk-00. This allows mixing the pre-shared key with the result from the handshake.

We're in the unfortunate situation where the Go team effectively froze crypto/tls (with the exception of security-related bugfixes). It doesn't seem like they are planning to implement any new features. quic-go already uses a fork of crypto/tls, however, I'd feel more confident in the security properties if we don't mess too much with the key schedule.

Add the ability to layer on some kind of trivial packet-based encryption protocol. We can use the same crypto as the current private network library as long as we use a new nonce per packet.

This is something we could do outside of quic-go. Effectively, we'd encrypt every QUIC packet that's being sent out. We'd either have to define a header that specifies the nonce of the packet, or use part of the QUIC packet payload as a nonce (for example the last 16 bytes of the packet, and leave those unencrypted). Aside from the obvious downside of double encryption, this would run against our efforts to make our traffic resemble commonly used internet protocols though.

Stebalien commented 5 years ago

I'm leaning towards just encrypting each packet with a format: <nonce><enc-message><hmac>. That's what we do in go-libp2p-pnet (well, minus the MAC; we're relying on the actual transport for authentication in that case...).

Stebalien commented 4 years ago

Having some form of private network (symmetric key) support in QUIC is needed before we can switch over to it by default. What's the status here? Can we mix in a symmetric key or are we going to have to introduce a packet-based pnet implementation?

marten-seemann commented 4 years ago

There's not been any progress in Go's standard library TLS implementation, which we'd need to mix in symmetric keys.

Is there a third option here? Can we do the encryption on a stream level?

Stebalien commented 4 years ago

Is there a third option here? Can we do the encryption on a stream level?

Not really. We'd leak the peer IDs at that point. We can probably find some kind of dead-simple symmetric-key encrypted packet transport. I'll ask on #libp2p to see if someone knows of a good one.

marten-seemann commented 4 years ago

This might be a crazy idea. What about only encrypting the certificate (or the libp2p extension in the certificate)?

Stebalien commented 4 years ago

That would definitely hide the peers. However:

Thoughts @Kubuxu? You implemented the original version.

marten-seemann commented 4 years ago

Another option would be to only encrypt Handshake packets. Let me explain: QUIC uses different packet types during the Handshake:

All packet types are identifiable from the wire image, i.e. no knowledge of the keys is necessary. Every packet type starts with a header, which, depending on the packet type, includes information like connection ID(s), QUIC version etc.

If we encrypt the payload (i.e. everything that comes after the QUIC header) of Handshake packets, we get the following properties:

However, an active attacker would notice that something's going on when trying to dial such a peer, since it would receive undecryptable Handshake packets. That might allow him to conclude that the node might be using a private network. This applies to most of the other solutions as well though.

I'm not sure if I prefer this solution. Encrypting the libp2p extension still seems a bit cleaner to me than modifying already encrypted packets, if we can live with exposing the fact that a node is (probably) running in private network mode.

marten-seemann commented 4 years ago

@Stebalien, @Kubuxu How can we move forward with this issue? Looks like we have a few different options, each with its pros and cons, and we need to make a decide which one we'll go with.

marten-seemann commented 4 years ago

Copying from Slack. @raulk raised the following point:

The problem with encrypting only the certificate is that if I happen to get access to a node’s private TLS key (single-use or not, as per libp2p-tls spec), and I record the certificate sent in a handshake, I could replay the encrypted certificate as-is on a new connection, and I would’ve bypassed the PSK security entirely, and I’d be able to interact with any node in the pnet using only the private key I obtained.

(without ever knowing the PSK)

Kubuxu commented 2 years ago

Hey, I missed this. I will review in next few days.

marten-seemann commented 2 years ago

@Kubuxu Nothing new here. I just moved the issue to go-libp2p because we've moved go-libp2p-quic-transport into go-libp2p. No idea why GitHub is sending out notifications for that :(