[Nick is writing this document as a summary of Tor's TLS annoyances, for the consumption of people who are working on the TLS protocol and would like to help Tor. It's a draft; I don't promise that it's accurate.]
For a few reasons, TLS as it stands is a difficult protocol for Tor to use.
REASON 1: IT IS HARD TO LOOK LIKE OTHER TLS INSTANCES FROM THE POV OF PASSIVE ATTACKS
The first is that TLS exists in a stupendously vast number of externally visible profiles. If you're running a protocol that an external censor would like to detect and block, it is not enough to simply use your TLS library in a sensible way. Instead, you need to pick a popular application that the censor doesn't want to block, and your clients need to emulate its clients, and your servers need to emulate its servers.
I'll go through the history of the stupid ways that Tor has tried to use TLS over the years. This will read like a comedy of errors; please don't judge our missteps too harshly.
Stage 1: (Tor versions 0.0.1 through 0.2.0.19)
What we would really like to do, if we didn't need to worry about censorship, is to have a profile where the client declared a list of ciphersuites using strong ciphers and some kind of ephemeral key agreement mechanism. The server should always send a two-certificate chain, with a longer-term server identity key signing a shorter-term link key. The link key should change on the order of hours to days. The client should either send no certificates, or a similar two-certificate chain, depending on whether it itself is a Tor server or not. We know that our data is all encrypted, and therefore uncompressible, so we don't want to use compression. We have no need for vhost support.
In our X.509 certificates, we really don't need to use any fields besides the public key, the serial number (for correctness), and the validAfter/validUntil fields.
In our earliest versions, we just set the cipher list to what we wanted to use, and disabled the extensions we weren't using. We did ephemeral key agreeement using the 1024-bit DH group specified in RFC RFC2409, section 6.2.
We made some concessions to avoiding fingerprinting during this phase. For example, we removed some fixed strings from our certificates' DNs.
Stage 2: (Tor 0.2.0.20 through 0.2.3.6)
Our particular use of two-certificate chains, our unusual cipher list, and our our funny-looking certs made Tor pretty easy to profile.
So we switched to an approach where we would begin by sending a list of ciphers hacked to match the list sent by Firefox, a server would reply with a single certificate, and then the client would immediately renegotiate. Upon the renegotiation, the server and client would pick the ciphersuites they really wanted to use, and exchange their real certificates.
To avoid the most trivial certificate-based attacks, we began generating bogus domain names (e.g., www.asjdioyzmohodf.net) and sticking them in the commonName part of the certificates.
Because we still needed to support old clients, we had the server detect whether the client was sending a new-style cipher list, and if so, reply using a single cert and expect renegotiation. If the client sent the old cipher list, we'd have to send a two-certs chain and not expect renegotiation.
In retrospect, this approach was ill-conceived for a few reasons. Having the server send different certificates depending on the contents of the ClientHello was hard to implement. (To match firefox, we also needed to feign support for the bogus nonstandard 0xfeff SSL3_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, which was hard to do.) Furthermore, it is trivial to detect a renegotiation by looking at record headers [*], and our renegotiations were apparently unusual enough to provoke interest.
When Marsh Ray's attack against renegotiation came out in 2009, and everybody who could possibly turn off renegotiation did so, our use of renegotiation stood out even more, especially since we had to keep doing it even when built with versions of OpenSSL that didn't support RFC 5746. [**]
In 2011, Iran blocked Tor based on our choice of Diffie-Hellman parameters. In version 0.2.2.22-alpha, we switched to copy the fixed DH parameters from Apache's mod_ssl, since we figured those were probably in more popular use.
[*] At this point, we hadn't actually learned very much about TLS internals: we were treating TLS as an idealized black-block encrypted transport. Obviously, this was a mistake on our part. We honestly hadn't imagined that the protocol would send record types and sizes in the clear, since that's so obviously an unnecessary information leak. When we found out about the renegotiation issues, when the TLS renegotiation MITM bug hit in 2009, and when the BEAST attack hit, we realized we needed to know a lot more about TLS internals to actually use it effectively.
[**] Doing so is safe for us: https://lists.torproject.org/pipermail/tor-talk/2009-November/015864.html
Stage 3: (Tor 0.2.3.6 to present)
When we started getting detected and blocked based on our use of renegotiation, we switched to an improved handshake, where the outer TLS handshake is meant to look as simple as possible (one cert of arbitrary type, no renegotiation, any ciphersuite with ephemeral keys is allowed). To get the authentication we need, we do a completely ugly hack and authenticate within the Tor protocol, by exchanging a signature of a MAC of some of the TLS connection parameters, and passing the certs we actually want. This approach allows us to have our original TLS handshake contain (nearly) anything we want it to, and still get the authentication we would actually prefer.
This duplicates TLS functionality, and we wouldn't need to be doing it if we didn't need to blend in with other TLS handshakes.
There are more features that are getting used to distinguish Tor from other TLS traffic. They include:
- Our cipher list matches that of a somewhat out-of-date ff.
- We don't send the same extensions in the same order as NSS does.
- Although our protocol provides support for arbitrary certificates, the software doesn't yet allow you to drop in (say) a CA-signed certificate to present on the wire.
We're hoping to address these in 0.2.4.x.
REASON 2: IT IS HARD TO RESIST ACTIVE PROBING
Most censorious attackers are passive, and seem to be using modified commercial software to detect and block Tor connections. On the other hand, the most sophisticated attacker (i.e, the Great Firewall) is using a hybrid passive/active approach where they notice suspicious TLS connections and then launch connections themselves to see whether there's a Tor node on the other side.
To resist this, we want to move to a circumstance where the client needs to know some secret or other kind of authenticator in order to complete the handshake ... or to even confirm that it isn't talking to a regular webserver. The presentation of this information can't be visible to a passive observer.
SOME OBVIOUS AND NOT-SO-OBVIOUS POINTS:
There are, as near as I can tell, two main classes of censorious adversary: the GFW, and everybody else. Everybody else seems either to rely on commercial censorware, or on home-brewed censorware that's no better than the commercial censorware.
The GFW maintainers seem to genuinely want to block Tor whenever it gets too easy to use to circumvent the firewall, or whenever there's a big politically significant event coming up.
The commercial censorware providers, on the other hand, seem to only care about blocking Tor when their customers complain, and only to the minimal extent necessary. In many cases, they have chosen the simplest-to-change protocol signature that they could possibly have chosen. This pattern of decisions might mean that their engineers disapprove of blocking Tor. More likely, it means that they are keeping their customers on an upgrade treadmill, to ensure that no single version of the software will block next year's version of Tor. Even likelier than that, their engineers are just being lazy, and adopting the simplest solution they can find so they can close the "Tor works" bug and get on to their next task.
We have had remarkable luck adding a trivial superencipherment layer to the TLS stream. Even though this layer could be trivially removed by any adversary who knows what to look for, and has an AES implementation, it's still working okay.
In Tor, every client starts out (or could be made to start out) knowing the public key of the server it wants to connect to. This might be good for something.
We know we're in an arms race. If we get rid of all the protocol signatures expressible in terms of looking for bytes on the connection, we'll still have to care about TCP packet boundaries and timing attacks.
In the long run, to avoid certain performance problems, we are considering moving server<->server communications to a UDP-based protocol, and using TLS-over-TCP only for client<->server communications, and in cases where it's necessary for client . Any such change is, however, a number of years out.