mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-11 05:33:47 +01:00
380d00246b
I propose a backward-compatible change to the Tor connection establishment protocol to avoid the use of TLS renegotiation. Rather than doing a TLS renegotiation to exchange certificates and authenticate the original handshake, this proposal takes an approach similar to Steven Murdoch's proposal 124, and uses Tor cells to authenticate the parties' identities once the initial TLS handshake is finished.
402 lines
18 KiB
Plaintext
402 lines
18 KiB
Plaintext
Filename: 169-eliminating-renegotiation.txt
|
|
Title: Eliminate TLS renegotiation for the Tor connection handshake
|
|
Author: Nick Mathewson
|
|
Created: 27-Jan-2010
|
|
Status: Draft
|
|
Target: 0.2.2
|
|
|
|
1. Overview
|
|
|
|
I propose a backward-compatible change to the Tor connection
|
|
establishment protocol to avoid the use of TLS renegotiation.
|
|
|
|
Rather than doing a TLS renegotiation to exchange certificates
|
|
and authenticate the original handshake, this proposal takes an
|
|
approach similar to Steven Murdoch's proposal 124, and uses Tor
|
|
cells to finish authenticating the parties' identities once the
|
|
initial TLS handshake is finished.
|
|
|
|
Terminological note: I use "client" below to mean the Tor
|
|
instance (a client or a relay) that initiates a TLS connection,
|
|
and "server" to mean the Tor instance (a relay) that accepts it.
|
|
|
|
2. Motivation and history
|
|
|
|
In the original Tor TLS connection handshake protocol ("V1", or
|
|
"two-cert"), parties that wanted to authenticate provided a
|
|
two-cert chain of X.509 certificates during the handshake setup
|
|
phase. Every party that wanted to authenticate sent these
|
|
certificates.
|
|
|
|
In the current Tor TLS connection handshake protocol ("V2", or
|
|
"renegotiating"), the parties begin with a single certificate
|
|
sent from the server (responder) to the client (initiator), and
|
|
then renegotiated to a two-certs-from-each-authenticating party.
|
|
We made this change to make Tor's handshake look like a browser
|
|
speaking SSL to a webserver. (See proposal 130, and
|
|
tor-spec.txt.) To tell whether to use the V1 or V2 handshake,
|
|
servers look at the list of ciphers sent by the client. (This is
|
|
ugly, but there's not much else in the ClientHello that they can
|
|
look at.) If the list contains any cipher not used by the V1
|
|
protocol, the server sends back a single cert and expects a
|
|
renegotiation. If the client gets back a single cert, then it
|
|
withholds its own certificates until the TLS renegotiation phase.
|
|
|
|
In other words, initiator behavior now looks like this:
|
|
|
|
- Begin TLS negotiation with V2 cipher list; wait for
|
|
certificate(s).
|
|
- If we get a certificate chain:
|
|
- Then we are using the V1 handshake. Send our own
|
|
certificate chain as part of this initial TLS handshake
|
|
if we want to authenticate; otherwise, send no
|
|
certificates. When the handshake completes, check
|
|
certificates. We are now mutually authenticated.
|
|
|
|
Otherwise, if we get just a single certificate:
|
|
- Then we are using the V2 handshake. Do not send any
|
|
certificates during this handshake.
|
|
- When the handshake is done, immediately start a TLS
|
|
renegotiation. During the renegotiation, expect
|
|
a certificate chain from the server; send a certificate
|
|
chain of our own if we want to authenticate ourselves.
|
|
- After the renegotiation, check the certificates. Then
|
|
send (and expect) a VERSIONS cell from the other side to
|
|
establish the link protocol version.
|
|
|
|
And V2 responder behavior now looks like this:
|
|
|
|
- When we get a TLS ClientHello request, look at the cipher
|
|
list.
|
|
- If the cipher list contains only the V1 ciphersuites:
|
|
- Then we're doing a V1 handshake. Send a certificate
|
|
chain. Expect a possible client certificate chain in
|
|
response.
|
|
Otherwise, if we get other ciphersuites:
|
|
- We're using the V2 handshake. Send back a single
|
|
certificate and let the handshake complete.
|
|
- Do not accept any data until the client has renegotiated.
|
|
- When the client is renegotiating, send a certificate
|
|
chain, and expect (possibly multiple certificates in
|
|
reply).
|
|
- Check the certificates when the renegotiation is done.
|
|
Then exchange VERSIONS cells.
|
|
|
|
Late in 2009, researchers found a flaw in most application's use
|
|
of TLS renegotiation: Although TLS renegotiation does not
|
|
reauthenticate any information exchanged before the renegotiation
|
|
takes place, many applications were treating it as though it did,
|
|
and assuming that data sent _before_ the renegotiation was
|
|
authenticated with the credentials negotiated _during_ the
|
|
renegotiation. This problem was exacerbated by the fact that
|
|
most TLS libraries don't actually give you an obvious good way to
|
|
tell where the renegotiation occurred relative to the datastream.
|
|
Tor wasn't directly affected by this vulnerability, but its
|
|
aftermath hurts us in a few ways:
|
|
|
|
1) OpenSSL has disabled renegotiation by default, and created
|
|
a "yes we know what we're doing" option we need to set to
|
|
turn it back on. (Two options, actually: one for openssl
|
|
0.9.8l and one for 0.9.8m and later.)
|
|
|
|
2) Some vendors have removed all renegotiation support from
|
|
their versions of OpenSSL entirely, forcing us to tell
|
|
users to either replace their versions of OpenSSL or to
|
|
link Tor against a hand-built one.
|
|
|
|
3) Because of 1 and 2, I'd expect TLS renegotiation to become
|
|
rarer and rarer in the wild, making our own use stand out
|
|
more.
|
|
|
|
3. Design
|
|
|
|
3.1. The view in the large
|
|
|
|
Taking a cue from Steven Murdoch's proposal 124, I propose that
|
|
we move the work currently done by the TLS renegotiation step
|
|
(that is, authenticating the parties to one another) and do it
|
|
with Tor cells instead of with TLS.
|
|
|
|
Using _yet another_ variant response from the responder (server),
|
|
we allow the client to learn that doesn't need to rehandshake,
|
|
and it can use a cell-based authentication system. Once the
|
|
TLS handshake is done, the client and server exchange VERSIONS
|
|
cells to determine what link protocol version (including
|
|
handshake version). If they're using the handshake version
|
|
specified here, the client and server arrive at link protocol
|
|
version 3 (or higher), and use cells to exchange further
|
|
authentication information.
|
|
|
|
3.2. New TLS handshake variant
|
|
|
|
We already used the list of ciphers from the clienthello to
|
|
indicate whether the client can speak the V2 ("renegotiating")
|
|
handshake or later, so we can't encode more information there.
|
|
|
|
We can, however, change the DN in the certificate passed by the
|
|
server to back the client. Currently, all V2 certificates are
|
|
generated with CN values ending with ".net". I propose that we
|
|
have the ".net" commonName ending reserved to indicate the V2
|
|
protocol, and use commonName values ending with ".com" to
|
|
indicate the V3 ("minimal") handshake described herein.
|
|
|
|
Now, once the initial TLS handshake is done, the client can look
|
|
at the server's certificate(s). If there is a certificate chain,
|
|
the handshake is V1. If there is a single certificate whose
|
|
subject commonName ends in ".net", the handshake is V2 and the
|
|
client should try to renegotiate as it would currently.
|
|
Otherwise, the client should assume that the handshake is V3+.
|
|
[Servers should _only_ send ".com" addesses, to allow room for
|
|
more signaling in the future.]
|
|
|
|
3.3. Authenticating inside Tor
|
|
|
|
Once the TLS handshake is finished, if the client renegotiates,
|
|
then the server should go on as it does currently.
|
|
|
|
If the client implements this proposal, however, and the server
|
|
has shown it can understand the V3+ handshake protocol, the
|
|
client immediately sends a VERSIONS cell to the server
|
|
and waits to receive a VERSIONS cell in return. We negotiate
|
|
the Tor link protocol version _before_ we proceed with the
|
|
negotiation, in case we need to change the authentication
|
|
protocol in the future.
|
|
|
|
Once either party has seen the VERSIONS cell from the other, it
|
|
knows which version they will pick (that is, the highest version
|
|
shared by both parties' VERSIONS cells). All Tor instances using
|
|
the handshake protocol described in 3.2 MUST support at least
|
|
link protocol version 3 as described here.
|
|
|
|
On learning the link protocol, the server then sends the client a
|
|
CERT cell and a NETINFO cell. If the client wants to
|
|
authenticate to the server, it sends a CERT cell, an AUTHENTICATE
|
|
cell, and a NETINFO cell, or it may simply send a NETINFO cell if
|
|
it does not want to authenticate.
|
|
|
|
The CERT cell describes the keys that a Tor instance is claiming
|
|
to have. It is a variable-length cell. Its payload format is:
|
|
|
|
N: Number of certs in cell [1 octet]
|
|
N times:
|
|
CLEN [2 octets]
|
|
Certificate [CLEN octets]
|
|
|
|
Any extra octets at the end of a CERT cell MUST be ignored.
|
|
|
|
Each certificate has the form:
|
|
|
|
CertType [1 octet]
|
|
CertPurpose [1 octet]
|
|
PublicKeyLen [2 octets]
|
|
PublicKey [PublicKeyLen octets]
|
|
NotBefore [4 octets]
|
|
NotAfter [4 octets]
|
|
SignerID [HASH256_LEN octets]
|
|
SignatureLen [2 octets]
|
|
Signature [SignatureLen octets]
|
|
|
|
where CertType is 1 (meaning "RSA/SHA256")
|
|
CertPurpose is 1 (meaning "link certificate")
|
|
PublicKey is the DER encoding of the ASN.1 representation
|
|
of the RSA key of the subject of this certificate,
|
|
NotBefore is a time in HOURS since January 1, 1970, 00:00
|
|
UTC before which this certificate should not be
|
|
considered valid.
|
|
NotAfter is a time in HOURS since January 1, 1970, 00:00
|
|
UTC after which this certificate should not be
|
|
considered valid.
|
|
SignerID is the SHA-256 digest of the public key signing
|
|
this certificate
|
|
and Signature is the signature of the all other fields in
|
|
this certificate, using SHA256 as described in proposal
|
|
158.
|
|
|
|
While authenticating, a server need send only a self-signed
|
|
certificate for its identity key. (Its TLS certificate already
|
|
contains its link key signed by its identity key.) A client that
|
|
wants to authenticate MUST send two certificates: one containing
|
|
a public link key signed by its identity key, and one self-signed
|
|
cert for its identity.
|
|
|
|
Tor instances MUST ignore any certificate with an unrecognized
|
|
CertType or CertPurpose.
|
|
|
|
The AUTHENTICATE cell proves to the server that the client with
|
|
whom it completed the initial TLS handshake is the one possessing
|
|
the link public key in its certificate. It is a variable-length
|
|
cell. Its contents are:
|
|
|
|
SignatureType [2 octets]
|
|
SignatureLen [2 octets]
|
|
Signature [SignatureLen octets]
|
|
|
|
where SignatureType is 1 (meaning "RSA-SHA256") and Signature is
|
|
an RSA-SHA256 signature of the HMAC-SHA256, using the TLS master
|
|
secret key as its key, of the following elements:
|
|
|
|
- The SignatureType field (0x00 0x01)
|
|
- The NUL terminated ASCII string: "Tor certificate verification"
|
|
- client_random, as sent in the Client Hello
|
|
- server_random, as sent in the Server Hello
|
|
|
|
Once the above handshake is complete, the client knows (from the
|
|
initial TLS handshake) that it has a secure connection to an
|
|
entity that controls a given link public key, and knows (from the
|
|
CERT cell) that the link public key is a valid public key for a
|
|
given Tor identity.
|
|
|
|
If the client authenticates, the server learns from the CERT cell
|
|
that a given Tor identity has a given current public link key.
|
|
From the AUTHENTICATE cell, it knows that an entity with that
|
|
link key knows the master secret for the TLS connection, and
|
|
hence must be the party with whom it's talking, if TLS works.
|
|
|
|
3.4. Security checks
|
|
|
|
If the TLS handshake indicates a V2 or V3+ connection, the server
|
|
MUST reject any connection from the client that does not begin
|
|
with either a renegotiation attempt or a VERSIONS cell containing
|
|
at least link protocol version "3". If the TLS handshake
|
|
indicates a V3+ connection, the client MUST reject any connection
|
|
where the server sends anything before the client has sent a
|
|
VERSIONS cell, and any connection where the VERSIONS cell does
|
|
not contain at least link protocol version "3".
|
|
|
|
If link protocol version 3 is chosen:
|
|
|
|
Clients and servers MUST check that all digests and signatures
|
|
on the certificates in CERT cells they are given are as
|
|
described above.
|
|
|
|
After the VERSIONS cell, clients and servers MUST close the
|
|
connection if anything besides a CERT or AUTH cell is sent
|
|
before the
|
|
|
|
CERT or AUTHENTICATE cells anywhere after the first NETINFO
|
|
cell must be rejected.
|
|
|
|
... [write more here. What else?] ...
|
|
|
|
3.5. Summary
|
|
|
|
We now revisit the protocol outlines from section 2 to incorporate
|
|
our changes. New or modified steps are marked with a *.
|
|
|
|
The new initiator behavior now looks like this:
|
|
|
|
- Begin TLS negotiation with V2 cipher list; wait for
|
|
certificate(s).
|
|
- If we get a certificate chain:
|
|
- Then we are using the V1 handshake. Send our own
|
|
certificate chain as part of this initial TLS handshake
|
|
if we want to authenticate; otherwise, send no
|
|
certificates. When the handshake completes, check
|
|
certificates. We are now mutually authenticated.
|
|
Otherwise, if we get just a single certificate:
|
|
- Then we are using the V2 or the V3+ handshake. Do not
|
|
send any certificates during this handshake.
|
|
* When the handshake is done, look at the server's
|
|
certificate's subject commonName.
|
|
* If it ends with ".net", we're doing a V2 handshake:
|
|
- Immediately start a TLS renegotiation. During the
|
|
renegotiation, expect a certificate chain from the
|
|
server; send a certificate chain of our own if we
|
|
want to authenticate ourselves.
|
|
- After the renegotiation, check the certificates. Then
|
|
send (and expect) a VERSIONS cell from the other side
|
|
to establish the link protocol version.
|
|
* If it ends with anything else, assume a V3 or later
|
|
handshake:
|
|
* Send a VERSIONS cell, and wait for a VERSIONS cell
|
|
from the server.
|
|
* If we are authenticating, send CERT and AUTHENTICATE
|
|
cells.
|
|
* Send a NETINFO cell. Wait for a CERT and a NETINFO
|
|
cell from the server.
|
|
* If the CERT cell is a good cert signing the public
|
|
key in the x.509 certificate we got during the TLS
|
|
handshake, we connected to the server with that
|
|
identity key. Otherwise close the connection.
|
|
* Once the NETINFO cell arrives, continue as before.
|
|
|
|
And V3+ responder behavior now looks like this:
|
|
|
|
- When we get a TLS ClientHello request, look at the cipher
|
|
list.
|
|
|
|
- If the cipher list contains only the V1 ciphersuites:
|
|
- Then we're doing a V1 handshake. Send a certificate
|
|
chain. Expect a possible client certificate chain in
|
|
response.
|
|
Otherwise, if we get other ciphersuites:
|
|
- We're using the V2 handshake. Send back a single
|
|
certificate whose subject commonName ends with ".com",
|
|
and let the handshake complete.
|
|
* If the client does anything besides renegotiate or send a
|
|
VERSIONS cell, drop the connection.
|
|
- If the client renegotiates immediately, it's a V2
|
|
connection:
|
|
- When the client is renegotiating, send a certificate
|
|
chain, and expect (possibly multiple certificates in
|
|
reply).
|
|
- Check the certificates when the renegotiation is done.
|
|
Then exchange VERSIONS cells.
|
|
* Otherwise we got a VERSIONS cell and it's a V3 handshake.
|
|
* Send a VERSIONS cell, a CERT cell, an AUTHENTICATE
|
|
cell, and a NETINFO cell.
|
|
* Wait for the client to send cells in reply. If the
|
|
client sends a CERT and an AUTHENTICATE and a NETINFO,
|
|
use them to authenticate the client. If the client
|
|
sends a NETINFO, it is unauthenticated. If it sends
|
|
anything else before its NETINFO, it's rejected.
|
|
|
|
4. Numbers to assign
|
|
|
|
We need a version number for this link protocol. I've been
|
|
calling it "3".
|
|
|
|
We need to reserve command numbers for CERT and AUTH cells. I
|
|
suggest that in link protocol 3 and higher, we reserve command
|
|
numbers 128..240 for variable-length cells. (241-256 we can hold
|
|
for future extensions.
|
|
|
|
5. Efficiency
|
|
|
|
This protocol add a round-trip step when the client sends a
|
|
VERSIONS cell to the server, and waits for the {VERSIONS, CERT,
|
|
NETINFO} response in turn. (The server then waits for the
|
|
client's {NETINFO} or {CERT, AUTHENTICATE, NETINFO} reply,
|
|
but it would have already been waiting for the client's NETINFO,
|
|
so that's not an additional wait.)
|
|
|
|
This is actually fewer round-trip steps than required before for
|
|
TLS renegotiation, so that's a win.
|
|
|
|
6. Open questions:
|
|
|
|
- Should we use X.509 certificates instead of the certificate-ish
|
|
things we describe here? They are more standard, but more ugly.
|
|
|
|
- May we cache which certificates we've already verified? It
|
|
might leak in timing whether we've connected with a given server
|
|
before, and how recently.
|
|
|
|
- Is there a better secret than the master secret to use in the
|
|
AUTHENTICATE cell? Say, a portable one? Can we get at it for
|
|
other libraries besides OpenSSL?
|
|
|
|
- Does using the client_random and server_random data in the
|
|
AUTHENTICATE message actually help us? How hard is it to pull
|
|
them out of the OpenSSL data structure?
|
|
|
|
- Can we give some way for clients to signal "I want to use the
|
|
V3 protocol if possible, but I can't renegotiate, so don't give
|
|
me the V2"? Clients currently have a fair idea of server
|
|
versions, so they could potentially do the V3+ handshake with
|
|
servers that support it, and fall back to V1 otherwise.
|
|
|
|
- What should servers that don't have TLS renegotiation do? For
|
|
now, I think they should just get it. Eventually we can
|
|
deprecate the V2 handshake as we did with the V1 handshake.
|