mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-11 05:33:47 +01:00
Merge branch 'tls_ecdhe_rebased_v2'
This commit is contained in:
commit
3eb3900510
3
changes/dh_benchmarks
Normal file
3
changes/dh_benchmarks
Normal 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
26
changes/tls_ecdhe
Normal 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.
|
||||
|
@ -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)
|
||||
|
@ -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. */
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 "
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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. */
|
||||
|
@ -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,
|
||||
|
@ -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}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user