aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changes/tls_ecdhe24
-rw-r--r--src/common/tortls.c116
-rw-r--r--src/common/tortls.h1
-rw-r--r--src/or/connection_or.c9
4 files changed, 131 insertions, 19 deletions
diff --git a/changes/tls_ecdhe b/changes/tls_ecdhe
new file mode 100644
index 000000000..58a8f9069
--- /dev/null
+++ b/changes/tls_ecdhe
@@ -0,0 +1,24 @@
+ o Major features:
+
+ - Servers can now enable the ECDHE TLS ciphersuites when
+ available and appropriate. These ciphersuites, when used with
+ the P-256 elliptic curve, let us negotiate forward-secure TLS
+ secret keys more safely and more efficiently than with our
+ previous use of Diffie Hellman modulo a 1024-bit prime.
+
+ Enabling these ciphers was a little tricky, since for a long
+ time, clients had been claiming to support them without
+ actually doing so, in order to foil fingerprinting. But with
+ the client-side implementation of proposal 198 in
+ 0.2.3.17-beta, clients can now match the ciphers from recent
+ firefox versions *and* list the ciphers they actually mean, so
+ servers can believe such clients when they advertise ECDHE
+ support in their TLS ClientHello messages.
+
+ This feature requires clients running 0.2.3.17-beta or later,
+ and requires both sides to be running OpenSSL 1.0.0 or later
+ with ECC support. OpenSSL 1.0.1, with the compile-time option
+ "enable-ec_nistp_64_gcc_128", is highly recommended.
+ Implements the server side of proposal 198; closes ticket
+ 7200.
+
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 4513689cf..bba89268e 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -678,11 +678,42 @@ tor_tls_create_certificate(crypto_pk_t *rsa,
#undef SERIAL_NUMBER_SIZE
}
-/** List of ciphers that servers should select from.*/
+/** List of ciphers that servers should select from when the client might be
+ * claiming extra unsupported ciphers in order to avoid fingerprinting. */
#define SERVER_CIPHER_LIST \
(TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" \
TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \
SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
+
+/** List of ciphers that servers should select from when we actually have
+ * our choice of what cipher to use. */
+const char UNRESTRICTED_SERVER_CIPHER_LIST[] =
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CHC_SHA
+ TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA ":"
+#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+ TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ":"
+#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256
+ TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256 ":"
+#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA
+ TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA ":"
+#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+#endif
+//#if TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA
+// TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA ":"
+//#endif
+ /* These next two are mandatory. */
+ TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":"
+ TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":"
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA
+ TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA ":"
+#endif
+ SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA;
+
/* Note: to set up your own private testing network with link crypto
* disabled, set your Tors' cipher list to
* (SSL3_TXT_RSA_NULL_SHA). If you do this, you won't be able to communicate
@@ -1410,15 +1441,22 @@ prune_v2_cipher_list(void)
v2_cipher_list_pruned = 1;
}
+/* Return the name of the negotiated ciphersuite in use on <b>tls</b> */
+const char *
+tor_tls_get_ciphersuite_name(tor_tls_t *tls)
+{
+ return SSL_get_cipher(tls->ssl);
+}
+
/** Examine the client cipher list in <b>ssl</b>, and determine what kind of
* client it is. Return one of CIPHERS_ERR, CIPHERS_V1, CIPHERS_V2,
* CIPHERS_UNRESTRICTED.
**/
static int
-tor_tls_classify_client_ciphers(const SSL *ssl)
+tor_tls_classify_client_ciphers(const SSL *ssl,
+ STACK_OF(SSL_CIPHER) *peer_ciphers)
{
int i, res;
- SSL_SESSION *session;
tor_tls_t *tor_tls;
if (PREDICT_UNLIKELY(!v2_cipher_list_pruned))
prune_v2_cipher_list();
@@ -1429,20 +1467,15 @@ tor_tls_classify_client_ciphers(const SSL *ssl)
/* If we reached this point, we just got a client hello. See if there is
* a cipher list. */
- if (!(session = SSL_get_session((SSL *)ssl))) {
- log_info(LD_NET, "No session on TLS?");
- res = CIPHERS_ERR;
- goto done;
- }
- if (!session->ciphers) {
+ if (!peer_ciphers) {
log_info(LD_NET, "No ciphers on session");
res = CIPHERS_ERR;
goto done;
}
/* Now we need to see if there are any ciphers whose presence means we're
* dealing with an updated Tor. */
- for (i = 0; i < sk_SSL_CIPHER_num(session->ciphers); ++i) {
- SSL_CIPHER *cipher = sk_SSL_CIPHER_value(session->ciphers, i);
+ for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) {
+ SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
const char *ciphername = SSL_CIPHER_get_name(cipher);
if (strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) &&
strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) &&
@@ -1458,8 +1491,8 @@ tor_tls_classify_client_ciphers(const SSL *ssl)
v2_or_higher:
{
const uint16_t *v2_cipher = v2_cipher_list;
- for (i = 0; i < sk_SSL_CIPHER_num(session->ciphers); ++i) {
- SSL_CIPHER *cipher = sk_SSL_CIPHER_value(session->ciphers, i);
+ for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) {
+ SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
uint16_t id = cipher->id & 0xffff;
if (id == 0x00ff) /* extended renegotiation indicator. */
continue;
@@ -1480,8 +1513,8 @@ tor_tls_classify_client_ciphers(const SSL *ssl)
{
smartlist_t *elts = smartlist_new();
char *s;
- for (i = 0; i < sk_SSL_CIPHER_num(session->ciphers); ++i) {
- SSL_CIPHER *cipher = sk_SSL_CIPHER_value(session->ciphers, i);
+ for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) {
+ SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
const char *ciphername = SSL_CIPHER_get_name(cipher);
smartlist_add(elts, (char*)ciphername);
}
@@ -1504,8 +1537,56 @@ tor_tls_classify_client_ciphers(const SSL *ssl)
static int
tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
{
- return tor_tls_classify_client_ciphers(ssl) >= CIPHERS_V2;
+ SSL_SESSION *session;
+ if (!(session = SSL_get_session((SSL *)ssl))) {
+ log_info(LD_NET, "No session on TLS?");
+ return CIPHERS_ERR;
+ }
+
+ return tor_tls_classify_client_ciphers(ssl, session->ciphers) >= CIPHERS_V2;
+}
+
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0)
+/** Callback to get invoked on a server after we've read the list of ciphers
+ * the client supports, but before we pick our own ciphersuite.
+ *
+ * We can't abuse an info_cb for this, since by the time one of the
+ * client_hello info_cbs is called, we've already picked which ciphersuite to
+ * use.
+ *
+ * Technically, this function is an abuse of this callback, since the point of
+ * a session_secret_cb is to try to set up and/or verify a shared-secret for
+ * authentication on the fly. But as long as we return 0, we won't actually be
+ * setting up a shared secret, and all will be fine.
+ */
+static int
+tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len,
+ STACK_OF(SSL_CIPHER) *peer_ciphers,
+ SSL_CIPHER **cipher, void *arg)
+{
+ (void) secret;
+ (void) secret_len;
+ (void) peer_ciphers;
+ (void) cipher;
+ (void) arg;
+
+ if (tor_tls_classify_client_ciphers(ssl, peer_ciphers) ==
+ CIPHERS_UNRESTRICTED) {
+ SSL_set_cipher_list(ssl, UNRESTRICTED_SERVER_CIPHER_LIST);
+ }
+
+ SSL_set_session_secret_cb(ssl, NULL, NULL);
+
+ return 0;
+}
+static void
+tor_tls_setup_session_secret_cb(tor_tls_t *tls)
+{
+ SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL);
}
+#else
+#define tor_tls_setup_session_secret_cb(tls) STMT_NIL
+#endif
/** Invoked when a TLS state changes: log the change at severity 'debug' */
static void
@@ -1773,6 +1854,9 @@ tor_tls_new(int sock, int isServer)
SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback);
}
+ if (isServer)
+ tor_tls_setup_session_secret_cb(result);
+
/* Not expected to get called. */
tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object");
return result;
diff --git a/src/common/tortls.h b/src/common/tortls.h
index 7bc6c8e76..8881827ce 100644
--- a/src/common/tortls.h
+++ b/src/common/tortls.h
@@ -129,6 +129,7 @@ int tor_tls_cert_is_valid(int severity,
const tor_cert_t *cert,
const tor_cert_t *signing_cert,
int check_rsa_1024);
+const char *tor_tls_get_ciphersuite_name(tor_tls_t *tls);
#endif
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 7ac4d1ee9..c2e4375db 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -1317,7 +1317,8 @@ connection_tls_continue_handshake(or_connection_t *conn)
if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) {
if (tor_tls_received_v3_certificate(conn->tls)) {
log_info(LD_OR, "Client got a v3 cert! Moving on to v3 "
- "handshake.");
+ "handshake with ciphersuite %s",
+ tor_tls_get_ciphersuite_name(conn->tls));
return connection_or_launch_v3_or_handshake(conn);
} else {
log_debug(LD_OR, "Done with initial SSL handshake (client-side)."
@@ -1641,10 +1642,12 @@ connection_tls_finish_handshake(or_connection_t *conn)
char digest_rcvd[DIGEST_LEN];
int started_here = connection_or_nonopen_was_started_here(conn);
- log_debug(LD_HANDSHAKE,"%s tls handshake on %p with %s done. verifying.",
+ log_debug(LD_HANDSHAKE,"%s tls handshake on %p with %s done, using "
+ "ciphersuite %s. verifying.",
started_here?"outgoing":"incoming",
conn,
- safe_str_client(conn->base_.address));
+ safe_str_client(conn->base_.address),
+ tor_tls_get_ciphersuite_name(conn->tls));
directory_set_dirty();