Merge branch 'tls_ecdhe_rebased_v2'

This commit is contained in:
Nick Mathewson 2012-12-25 22:10:57 -05:00
commit 3eb3900510
10 changed files with 481 additions and 36 deletions

3
changes/dh_benchmarks Normal file
View File

@ -0,0 +1,3 @@
o Minor features (testing):
- Add benchmarks for DH (1024-bit multiplicative group) and ECDH
(P-256) diffie-hellman handshakes to src/or/bench.

26
changes/tls_ecdhe Normal file
View File

@ -0,0 +1,26 @@
o Major features:
- Servers can now enable the ECDHE TLS ciphersuites when available
and appropriate. These ciphersuites 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.
By default, public servers prefer the (faster) P224 group, and
bridges prefer the (more common) P256 group; you can override this
with the TLSECGroup option.
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.

View File

@ -1527,6 +1527,12 @@ is non-zero):
**GeoIPv6File** __filename__::
A filename containing IPv6 GeoIP data, for use with by-country statistics.
**TLSECGroup** **P224**|**P256**::
What EC group should we try to use for incoming TLS connections?
P224 is faster, but makes us stand out more. Has no effect if
we're a client, or if our OpenSSL version lacks support for ECDHE.
(Default: P224 for public servers; P256 for bridges.)
**CellStatistics** **0**|**1**::
When this option is enabled, Tor writes statistics on the mean time that
cells spend in circuit queues to disk every 24 hours. (Default: 0)

View File

@ -127,6 +127,24 @@ typedef struct tor_tls_context_t {
crypto_pk_t *auth_key;
} tor_tls_context_t;
/** Return values for tor_tls_classify_client_ciphers.
*
* @{
*/
/** An error occurred when examining the client ciphers */
#define CIPHERS_ERR -1
/** The client cipher list indicates that a v1 handshake was in use. */
#define CIPHERS_V1 1
/** The client cipher list indicates that the client is using the v2 or the
* v3 handshake, but that it is (probably!) lying about what ciphers it
* supports */
#define CIPHERS_V2 2
/** The client cipher list indicates that the client is using the v2 or the
* v3 handshake, and that it is telling the truth about what ciphers it
* supports */
#define CIPHERS_UNRESTRICTED 3
/** @} */
#define TOR_TLS_MAGIC 0x71571571
/** Holds a SSL object and its associated data. Members are only
@ -152,6 +170,9 @@ struct tor_tls_t {
* one certificate). */
/** True iff we should call negotiated_callback when we're done reading. */
unsigned int got_renegotiate:1;
/** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't
* called that function yet. */
int8_t client_cipher_list_type;
/** Incremented every time we start the server side of a handshake. */
uint8_t server_handshake_count;
size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last
@ -215,9 +236,11 @@ static X509* tor_tls_create_certificate(crypto_pk_t *rsa,
static int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
crypto_pk_t *identity,
unsigned int key_lifetime,
unsigned int flags,
int is_client);
static tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
unsigned int key_lifetime,
unsigned int flags,
int is_client);
static int check_cert_lifetime_internal(int severity, const X509 *cert,
int past_tolerance, int future_tolerance);
@ -505,6 +528,37 @@ tor_tls_init(void)
SSLeay_version(SSLEAY_VERSION), version);
}
#if (SIZEOF_VOID_P >= 8 && \
!defined(OPENSSL_NO_EC) && \
OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1))
if (version >= OPENSSL_V_SERIES(1,0,1)) {
/* Warn if we could *almost* be running with much faster ECDH.
If we're built for a 64-bit target, using OpenSSL 1.0.1, but we
don't have one of the built-in __uint128-based speedups, we are
just one build operation away from an accelerated handshake.
(We could be looking at OPENSSL_NO_EC_NISTP_64_GCC_128 instead of
doing this test, but that gives compile-time options, not runtime
behavior.)
*/
EC_KEY *key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
const EC_GROUP *g = key ? EC_KEY_get0_group(key) : NULL;
const EC_METHOD *m = g ? EC_GROUP_method_of(g) : NULL;
const int warn = (m == EC_GFp_simple_method() ||
m == EC_GFp_mont_method() ||
m == EC_GFp_nist_method());
EC_KEY_free(key);
if (warn)
log_notice(LD_GENERAL, "We were built to run on a 64-bit CPU, with "
"OpenSSL 1.0.1 or later, but with a version of OpenSSL "
"that apparently lacks accelerated support for the NIST "
"P-224 and P-256 groups. Building openssl with such "
"support (using the enable-ec_nistp_64_gcc_128 option "
"when configuring it) would make ECDH much faster.");
}
#endif
tor_tls_allocate_tor_tls_object_ex_data_index();
tls_library_is_initialized = 1;
@ -657,11 +711,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
@ -1014,17 +1099,20 @@ tor_tls_context_incref(tor_tls_context_t *ctx)
/** Create new global client and server TLS contexts.
*
* If <b>server_identity</b> is NULL, this will not generate a server
* TLS context. If <b>is_public_server</b> is non-zero, this will use
* TLS context. If TOR_TLS_CTX_IS_PUBLIC_SERVER is set in <b>flags</b>, use
* the same TLS context for incoming and outgoing connections, and
* ignore <b>client_identity</b>. */
* ignore <b>client_identity</b>. If one of TOR_TLS_CTX_USE_ECDHE_P{224,256}
* is set in <b>flags</b>, use that ECDHE group if possible; otherwise use
* the default ECDHE group. */
int
tor_tls_context_init(int is_public_server,
tor_tls_context_init(unsigned flags,
crypto_pk_t *client_identity,
crypto_pk_t *server_identity,
unsigned int key_lifetime)
{
int rv1 = 0;
int rv2 = 0;
const int is_public_server = flags & TOR_TLS_CTX_IS_PUBLIC_SERVER;
if (is_public_server) {
tor_tls_context_t *new_ctx;
@ -1034,7 +1122,7 @@ tor_tls_context_init(int is_public_server,
rv1 = tor_tls_context_init_one(&server_tls_context,
server_identity,
key_lifetime, 0);
key_lifetime, flags, 0);
if (rv1 >= 0) {
new_ctx = server_tls_context;
@ -1051,6 +1139,7 @@ tor_tls_context_init(int is_public_server,
rv1 = tor_tls_context_init_one(&server_tls_context,
server_identity,
key_lifetime,
flags,
0);
} else {
tor_tls_context_t *old_ctx = server_tls_context;
@ -1064,6 +1153,7 @@ tor_tls_context_init(int is_public_server,
rv2 = tor_tls_context_init_one(&client_tls_context,
client_identity,
key_lifetime,
flags,
1);
}
@ -1080,10 +1170,12 @@ static int
tor_tls_context_init_one(tor_tls_context_t **ppcontext,
crypto_pk_t *identity,
unsigned int key_lifetime,
unsigned int flags,
int is_client)
{
tor_tls_context_t *new_ctx = tor_tls_context_new(identity,
key_lifetime,
flags,
is_client);
tor_tls_context_t *old_ctx = *ppcontext;
@ -1107,7 +1199,7 @@ tor_tls_context_init_one(tor_tls_context_t **ppcontext,
*/
static tor_tls_context_t *
tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
int is_client)
unsigned flags, int is_client)
{
crypto_pk_t *rsa = NULL, *rsa_auth = NULL;
EVP_PKEY *pkey = NULL;
@ -1224,6 +1316,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
}
SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_DH_USE);
SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_ECDH_USE);
#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
SSL_CTX_set_options(result->ctx,
@ -1274,6 +1367,26 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
SSL_CTX_set_tmp_dh(result->ctx, crypto_dh_get_dh_(dh));
crypto_dh_free(dh);
}
#if (!defined(OPENSSL_NO_EC) && \
OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0))
if (! is_client) {
int nid;
EC_KEY *ec_key;
if (flags & TOR_TLS_CTX_USE_ECDHE_P224)
nid = NID_secp224r1;
else if (flags & TOR_TLS_CTX_USE_ECDHE_P256)
nid = NID_X9_62_prime256v1;
else if (flags & TOR_TLS_CTX_IS_PUBLIC_SERVER)
nid = NID_X9_62_prime256v1;
else
nid = NID_secp224r1;
/* Use P-256 for ECDHE. */
ec_key = EC_KEY_new_by_curve_name(nid);
if (ec_key != NULL) /*XXXX Handle errors? */
SSL_CTX_set_tmp_ecdh(result->ctx, ec_key);
EC_KEY_free(ec_key);
}
#endif
SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER,
always_accept_verify_cb);
/* let us realloc bufs that we're writing from */
@ -1310,28 +1423,108 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
}
#ifdef V2_HANDSHAKE_SERVER
/** Return true iff the cipher list suggested by the client for <b>ssl</b> is
* a list that indicates that the client knows how to do the v2 TLS connection
* handshake. */
static int
tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address)
/* Here's the old V2 cipher list we sent from 0.2.1.1-alpha up to
* 0.2.3.17-beta. If a client is using this list, we can't believe the ciphers
* that it claims to support. We'll prune this list to remove the ciphers
* *we* don't recognize. */
static uint16_t v2_cipher_list[] = {
0xc00a, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA */
0xc014, /* TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA */
0x0039, /* TLS1_TXT_DHE_RSA_WITH_AES_256_SHA */
0x0038, /* TLS1_TXT_DHE_DSS_WITH_AES_256_SHA */
0xc00f, /* TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA */
0xc005, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA */
0x0035, /* TLS1_TXT_RSA_WITH_AES_256_SHA */
0xc007, /* TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA */
0xc009, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA */
0xc011, /* TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA */
0xc013, /* TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA */
0x0033, /* TLS1_TXT_DHE_RSA_WITH_AES_128_SHA */
0x0032, /* TLS1_TXT_DHE_DSS_WITH_AES_128_SHA */
0xc00c, /* TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA */
0xc00e, /* TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA */
0xc002, /* TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA */
0xc004, /* TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA */
0x0004, /* SSL3_TXT_RSA_RC4_128_MD5 */
0x0005, /* SSL3_TXT_RSA_RC4_128_SHA */
0x002f, /* TLS1_TXT_RSA_WITH_AES_128_SHA */
0xc008, /* TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA */
0xc012, /* TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA */
0x0016, /* SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA */
0x0013, /* SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA */
0xc00d, /* TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA */
0xc003, /* TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA */
0xfeff, /* SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA */
0x000a, /* SSL3_TXT_RSA_DES_192_CBC3_SHA */
0
};
/** Have we removed the unrecognized ciphers from v2_cipher_list yet? */
static int v2_cipher_list_pruned = 0;
/** Remove from v2_cipher_list every cipher that we don't support, so that
* comparing v2_cipher_list to a client's cipher list will give a sensible
* result. */
static void
prune_v2_cipher_list(void)
{
int i;
SSL_SESSION *session;
uint16_t *inp, *outp;
const SSL_METHOD *m = SSLv23_method();
inp = outp = v2_cipher_list;
while (*inp) {
unsigned char cipherid[2];
const SSL_CIPHER *cipher;
/* Is there no better way to do this? */
set_uint16(cipherid, htons(*inp));
cipher = m->get_cipher_by_char(cipherid);
if (cipher) {
tor_assert((cipher->id & 0xffff) == *inp);
*outp++ = *inp++;
} else {
inp++;
}
}
*outp = 0;
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,
STACK_OF(SSL_CIPHER) *peer_ciphers)
{
int i, res;
tor_tls_t *tor_tls;
if (PREDICT_UNLIKELY(!v2_cipher_list_pruned))
prune_v2_cipher_list();
tor_tls = tor_tls_get_by_ssl(ssl);
if (tor_tls && tor_tls->client_cipher_list_type)
return tor_tls->client_cipher_list_type;
/* 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?");
return 0;
}
if (!session->ciphers) {
if (!peer_ciphers) {
log_info(LD_NET, "No ciphers on session");
return 0;
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) &&
@ -1339,28 +1532,111 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address)
strcmp(ciphername, "(NONE)")) {
log_debug(LD_NET, "Got a non-version-1 cipher called '%s'", ciphername);
// return 1;
goto dump_list;
goto v2_or_higher;
}
}
return 0;
dump_list:
res = CIPHERS_V1;
goto done;
v2_or_higher:
{
const uint16_t *v2_cipher = v2_cipher_list;
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;
if (!id || id != *v2_cipher) {
res = CIPHERS_UNRESTRICTED;
goto dump_ciphers;
}
++v2_cipher;
}
if (*v2_cipher != 0) {
res = CIPHERS_UNRESTRICTED;
goto dump_ciphers;
}
res = CIPHERS_V2;
}
dump_ciphers:
{
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);
}
s = smartlist_join_strings(elts, ":", 0, NULL);
log_debug(LD_NET, "Got a non-version-1 cipher list from %s. It is: '%s'",
address, s);
log_debug(LD_NET, "Got a %s V2/V3 cipher list from %s. It is: '%s'",
(res == CIPHERS_V2) ? "fictitious" : "real", ADDR(tor_tls), s);
tor_free(s);
smartlist_free(elts);
}
return 1;
done:
if (tor_tls)
return tor_tls->client_cipher_list_type = res;
return res;
}
/** Return true iff the cipher list suggested by the client for <b>ssl</b> is
* a list that indicates that the client knows how to do the v2 TLS connection
* handshake. */
static int
tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
{
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
tor_tls_debug_state_callback(const SSL *ssl, int type, int val)
@ -1402,7 +1678,7 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
}
/* Now check the cipher list. */
if (tor_tls_client_is_using_v2_ciphers(ssl, ADDR(tls))) {
if (tor_tls_client_is_using_v2_ciphers(ssl)) {
if (tls->wasV2Handshake)
return; /* We already turned this stuff off for the first handshake;
* This is a renegotiation. */
@ -1627,6 +1903,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;
@ -1868,7 +2147,7 @@ tor_tls_finish_handshake(tor_tls_t *tls)
/* There doesn't seem to be a clear OpenSSL API to clear mode flags. */
tls->ssl->mode &= ~SSL_MODE_NO_AUTO_CHAIN;
#ifdef V2_HANDSHAKE_SERVER
if (tor_tls_client_is_using_v2_ciphers(tls->ssl, ADDR(tls))) {
if (tor_tls_client_is_using_v2_ciphers(tls->ssl)) {
/* This check is redundant, but back when we did it in the callback,
* we might have not been able to look up the tor_tls_t if the code
* was buggy. Fixing that. */

View File

@ -54,7 +54,12 @@ const char *tor_tls_err_to_string(int err);
void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz);
void tor_tls_free_all(void);
int tor_tls_context_init(int is_public_server,
#define TOR_TLS_CTX_IS_PUBLIC_SERVER (1u<<0)
#define TOR_TLS_CTX_USE_ECDHE_P256 (1u<<1)
#define TOR_TLS_CTX_USE_ECDHE_P224 (1u<<2)
int tor_tls_context_init(unsigned flags,
crypto_pk_t *client_identity,
crypto_pk_t *server_identity,
unsigned int key_lifetime);
@ -129,6 +134,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

View File

@ -372,6 +372,7 @@ static config_var_t option_vars_[] = {
OBSOLETE("TestVia"),
V(TokenBucketRefillInterval, MSEC_INTERVAL, "100 msec"),
V(Tor2webMode, BOOL, "0"),
V(TLSECGroup, STRING, NULL),
V(TrackHostExits, CSV, NULL),
V(TrackHostExitsExpire, INTERVAL, "30 minutes"),
OBSOLETE("TrafficShaping"),
@ -1193,6 +1194,9 @@ options_transition_requires_fresh_tls_context(const or_options_t *old_options,
return 1;
}
if (!opt_streq(old_options->TLSECGroup, new_options->TLSECGroup))
return 1;
return 0;
}
@ -2301,6 +2305,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
}
if (options->TLSECGroup && (strcasecmp(options->TLSECGroup, "P256") &&
strcasecmp(options->TLSECGroup, "P224"))) {
COMPLAIN("Unrecognized TLSECGroup: Falling back to the default.");
tor_free(options->TLSECGroup);
}
if (options->ExcludeNodes && options->StrictNodes) {
COMPLAIN("You have asked to exclude certain relays from all positions "
"in your circuits. Expect hidden services and other Tor "

View File

@ -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();

View File

@ -3854,6 +3854,8 @@ typedef struct {
int IPv6Exit; /**< Do we support exiting to IPv6 addresses? */
char *TLSECGroup; /**< One of "P256", "P224", or nil for auto */
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */

View File

@ -491,7 +491,18 @@ v3_authority_check_key_expiry(void)
int
router_initialize_tls_context(void)
{
return tor_tls_context_init(public_server_mode(get_options()),
unsigned int flags = 0;
const or_options_t *options = get_options();
if (public_server_mode(options))
flags |= TOR_TLS_CTX_IS_PUBLIC_SERVER;
if (options->TLSECGroup) {
if (!strcasecmp(options->TLSECGroup, "P256"))
flags |= TOR_TLS_CTX_USE_ECDHE_P256;
else if (!strcasecmp(options->TLSECGroup, "P224"))
flags |= TOR_TLS_CTX_USE_ECDHE_P224;
}
return tor_tls_context_init(flags,
get_tlsclient_identity_key(),
server_mode(get_options()) ?
get_server_identity_key() : NULL,

View File

@ -18,6 +18,15 @@ const char tor_git_revision[] = "";
#include "or.h"
#include "relay.h"
#include <openssl/opensslv.h>
#include <openssl/evp.h>
#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0)
#ifndef OPENSSL_NO_EC
#include <openssl/ec.h>
#include <openssl/ecdh.h>
#include <openssl/obj_mac.h>
#endif
#endif
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID)
static uint64_t nanostart;
@ -248,6 +257,91 @@ bench_cell_ops(void)
tor_free(cell);
}
static void
bench_dh(void)
{
const int iters = 1<<10;
int i;
uint64_t start, end;
reset_perftime();
start = perftime();
for (i = 0; i < iters; ++i) {
char dh_pubkey_a[DH_BYTES], dh_pubkey_b[DH_BYTES];
char secret_a[DH_BYTES], secret_b[DH_BYTES];
ssize_t slen_a, slen_b;
crypto_dh_t *dh_a = crypto_dh_new(DH_TYPE_TLS);
crypto_dh_t *dh_b = crypto_dh_new(DH_TYPE_TLS);
crypto_dh_generate_public(dh_a);
crypto_dh_generate_public(dh_b);
crypto_dh_get_public(dh_a, dh_pubkey_a, sizeof(dh_pubkey_a));
crypto_dh_get_public(dh_b, dh_pubkey_b, sizeof(dh_pubkey_b));
slen_a = crypto_dh_compute_secret(LOG_NOTICE,
dh_a, dh_pubkey_b, sizeof(dh_pubkey_b),
secret_a, sizeof(secret_a));
slen_b = crypto_dh_compute_secret(LOG_NOTICE,
dh_b, dh_pubkey_a, sizeof(dh_pubkey_a),
secret_b, sizeof(secret_b));
tor_assert(slen_a == slen_b);
tor_assert(!memcmp(secret_a, secret_b, slen_a));
crypto_dh_free(dh_a);
crypto_dh_free(dh_b);
}
end = perftime();
printf("Complete DH handshakes (1024 bit, public and private ops):\n"
" %f millisec each.\n", NANOCOUNT(start, end, iters)/1e6);
}
#if (!defined(OPENSSL_NO_EC) \
&& OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0))
#define HAVE_EC_BENCHMARKS
static void
bench_ecdh_impl(int nid, const char *name)
{
const int iters = 1<<10;
int i;
uint64_t start, end;
reset_perftime();
start = perftime();
for (i = 0; i < iters; ++i) {
char secret_a[DH_BYTES], secret_b[DH_BYTES];
ssize_t slen_a, slen_b;
EC_KEY *dh_a = EC_KEY_new_by_curve_name(nid);
EC_KEY *dh_b = EC_KEY_new_by_curve_name(nid);
EC_KEY_generate_key(dh_a);
EC_KEY_generate_key(dh_b);
slen_a = ECDH_compute_key(secret_a, DH_BYTES,
EC_KEY_get0_public_key(dh_b), dh_a,
NULL);
slen_b = ECDH_compute_key(secret_b, DH_BYTES,
EC_KEY_get0_public_key(dh_a), dh_b,
NULL);
tor_assert(slen_a == slen_b);
tor_assert(!memcmp(secret_a, secret_b, slen_a));
EC_KEY_free(dh_a);
EC_KEY_free(dh_b);
}
end = perftime();
printf("Complete ECDH %s handshakes (2 public and 2 private ops):\n"
" %f millisec each.\n", name, NANOCOUNT(start, end, iters)/1e6);
}
static void
bench_ecdh_p256(void)
{
bench_ecdh_impl(NID_X9_62_prime256v1, "P-256");
}
static void
bench_ecdh_p224(void)
{
bench_ecdh_impl(NID_secp224r1, "P-224");
}
#endif
typedef void (*bench_fn)(void);
typedef struct benchmark_t {
@ -263,6 +357,11 @@ static struct benchmark_t benchmarks[] = {
ENT(aes),
ENT(cell_aes),
ENT(cell_ops),
ENT(dh),
#ifdef HAVE_EC_BENCHMARKS
ENT(ecdh_p256),
ENT(ecdh_p224),
#endif
{NULL,NULL,0}
};