[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] r15173: Implement code to manually force the OpenSSL client cipher l (in tor/trunk: . doc doc/spec src/common)



Author: nickm
Date: 2008-06-12 18:39:13 -0400 (Thu, 12 Jun 2008)
New Revision: 15173

Added:
   tor/trunk/src/common/ciphers.inc
Modified:
   tor/trunk/
   tor/trunk/ChangeLog
   tor/trunk/doc/TODO
   tor/trunk/doc/spec/tor-spec.txt
   tor/trunk/src/common/Makefile.am
   tor/trunk/src/common/tortls.c
Log:
 r16215@tombo:  nickm | 2008-06-12 18:39:03 -0400
 Implement code to manually force the OpenSSL client cipher list to match the one recommended in proposal 124, *even if* we do not know all those ciphers.  This is a bit of a kludge, but it is at least decently well commented.



Property changes on: tor/trunk
___________________________________________________________________
 svk:merge ticket from /tor/trunk [r16215] on 49666b30-7950-49c5-bedf-9dc8f3168102

Modified: tor/trunk/ChangeLog
===================================================================
--- tor/trunk/ChangeLog	2008-06-12 21:36:09 UTC (rev 15172)
+++ tor/trunk/ChangeLog	2008-06-12 22:39:13 UTC (rev 15173)
@@ -19,6 +19,12 @@
       service, with the 30 seconds being the current voodoo saying that
       a descriptor is stable.
 
+  o Major features:
+    - Modify the list of ciphers advertised by OpenSSL in client mode
+      to even more closely resemble a common web browser.  We cheat a
+      little so that we can advertise ciphers that the locally
+      installed OpenSSL doesn't know about.
+
   o Minor features:
     - Allow separate log levels to be configured for different logging
       domains.  For example, this allows one to log all notices, warnings,
@@ -68,6 +74,8 @@
     - Never use OpenSSL compression: it wastes RAM and CPU trying to
       compress cells, which are basically all encrypted, compressed, or
       both.
+    - Use the TLS1 hostname extension to more closely resemble browser
+      behavior.
 
   o Code simplifications and refactoring:
     - Refactor code using connection_ap_handshake_attach_circuit() to

Modified: tor/trunk/doc/TODO
===================================================================
--- tor/trunk/doc/TODO	2008-06-12 21:36:09 UTC (rev 15172)
+++ tor/trunk/doc/TODO	2008-06-12 22:39:13 UTC (rev 15173)
@@ -105,10 +105,10 @@
       . Test
     - More back-end work:
 N     - Additional TLS-camouflage work (spoofing FF cipher suite, etc.)
-        - spoof the cipher suites
-        - spoof the extensions list
+        o spoof the cipher suites
+        o spoof the extensions list
         - red-team testing (a.k.a, look at a packet dump and compare),
-        - investigate the feasibility of handing connections off to a
+        . investigate the feasibility of handing connections off to a
           local apache if they don't look like Tor or if they don't
           portknock or whatever.
       - Get closer to downloading far fewer descriptors

Modified: tor/trunk/doc/spec/tor-spec.txt
===================================================================
--- tor/trunk/doc/spec/tor-spec.txt	2008-06-12 21:36:09 UTC (rev 15172)
+++ tor/trunk/doc/spec/tor-spec.txt	2008-06-12 22:39:13 UTC (rev 15173)
@@ -174,7 +174,10 @@
    handshake is complete, the initiator renegotiates the handshake, with each
    parties sending a two-certificate chain as in "certificates up-front".
    The initiator's ClientHello MUST include at least once ciphersuite not in
-   the list above.
+   the list above.  The responder SHOULD NOT select any ciphersuite besides
+   those in the list above.
+     [The above "should not" is because some of the ciphers that
+     clients list may be fake.]
 
    In "backwards-compatible renegotiation", the connection initiator's
    ClientHello MUST include at least one ciphersuite other than those listed

Modified: tor/trunk/src/common/Makefile.am
===================================================================
--- tor/trunk/src/common/Makefile.am	2008-06-12 21:36:09 UTC (rev 15172)
+++ tor/trunk/src/common/Makefile.am	2008-06-12 22:39:13 UTC (rev 15173)
@@ -13,4 +13,4 @@
 	$(libor_extra_source)
 libor_crypto_a_SOURCES = crypto.c aes.c tortls.c torgzip.c
 
-noinst_HEADERS = log.h crypto.h test.h util.h compat.h aes.h torint.h tortls.h strlcpy.c strlcat.c torgzip.h container.h ht.h mempool.h memarea.h
+noinst_HEADERS = log.h crypto.h test.h util.h compat.h aes.h torint.h tortls.h strlcpy.c strlcat.c torgzip.h container.h ht.h mempool.h memarea.h ciphers.inc

Added: tor/trunk/src/common/ciphers.inc
===================================================================
--- tor/trunk/src/common/ciphers.inc	                        (rev 0)
+++ tor/trunk/src/common/ciphers.inc	2008-06-12 22:39:13 UTC (rev 15173)
@@ -0,0 +1,143 @@
+/* This is an include file used to define the list of ciphers clients should
+ * advertise.  Before including it, you should define the CIPHER and XCPIHER
+ * macros. */
+#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
+    CIPHER(0xc00a, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
+#else
+   XCIPHER(0xc00a, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
+#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA
+    CIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA)
+#else
+   XCIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA)
+#endif
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA
+    CIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA)
+#else
+   XCIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA)
+#endif
+#ifdef TLS1_TXT_DHE_DSS_WITH_AES_256_SHA
+    CIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA)
+#else
+   XCIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA)
+#endif
+#ifdef TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA
+    CIPHER(0xc00f, TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA)
+#else
+   XCIPHER(0xc00f, TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA)
+#endif
+#ifdef TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA
+    CIPHER(0xc005, TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA)
+#else
+   XCIPHER(0xc005, TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA)
+#endif
+#ifdef TLS1_TXT_RSA_WITH_AES_256_SHA
+    CIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA)
+#else
+   XCIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA)
+#endif
+#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA
+    CIPHER(0xc007, TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA)
+#else
+   XCIPHER(0xc007, TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA)
+#endif
+#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
+    CIPHER(0xc009, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
+#else
+   XCIPHER(0xc009, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
+#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA
+    CIPHER(0xc011, TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA)
+#else
+   XCIPHER(0xc011, TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA)
+#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA
+    CIPHER(0xc013, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA)
+#else
+   XCIPHER(0xc013, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA)
+#endif
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_SHA
+    CIPHER(0x0033, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA)
+#else
+   XCIPHER(0x0033, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA)
+#endif
+#ifdef TLS1_TXT_DHE_DSS_WITH_AES_128_SHA
+    CIPHER(0x0032, TLS1_TXT_DHE_DSS_WITH_AES_128_SHA)
+#else
+   XCIPHER(0x0032, TLS1_TXT_DHE_DSS_WITH_AES_128_SHA)
+#endif
+#ifdef TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA
+    CIPHER(0xc00c, TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA)
+#else
+   XCIPHER(0xc00c, TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA)
+#endif
+#ifdef TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA
+    CIPHER(0xc00e, TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA)
+#else
+   XCIPHER(0xc00e, TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA)
+#endif
+#ifdef TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA
+    CIPHER(0xc002, TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA)
+#else
+   XCIPHER(0xc002, TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA)
+#endif
+#ifdef TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA
+    CIPHER(0xc004, TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA)
+#else
+   XCIPHER(0xc004, TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA)
+#endif
+#ifdef SSL3_TXT_RSA_RC4_128_MD5
+    CIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5)
+#else
+   XCIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5)
+#endif
+#ifdef SSL3_TXT_RSA_RC4_128_SHA
+    CIPHER(0x0005, SSL3_TXT_RSA_RC4_128_SHA)
+#else
+   XCIPHER(0x0005, SSL3_TXT_RSA_RC4_128_SHA)
+#endif
+#ifdef TLS1_TXT_RSA_WITH_AES_128_SHA
+    CIPHER(0x002f, TLS1_TXT_RSA_WITH_AES_128_SHA)
+#else
+   XCIPHER(0x002f, TLS1_TXT_RSA_WITH_AES_128_SHA)
+#endif
+#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA
+    CIPHER(0xc008, TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA)
+#else
+   XCIPHER(0xc008, TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA)
+#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA
+    CIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA)
+#else
+   XCIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA)
+#endif
+#ifdef SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA
+    CIPHER(0x0016, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
+#else
+   XCIPHER(0x0016, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
+#endif
+#ifdef SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA
+    CIPHER(0x0013, SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA)
+#else
+   XCIPHER(0x0013, SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA)
+#endif
+#ifdef TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA
+    CIPHER(0xc00d, TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA)
+#else
+   XCIPHER(0xc00d, TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA)
+#endif
+#ifdef TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA
+    CIPHER(0xc003, TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA)
+#else
+   XCIPHER(0xc003, TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA)
+#endif
+#ifdef SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA
+    CIPHER(0xfeff, SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA)
+#else
+   XCIPHER(0xfeff, SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA)
+#endif
+#ifdef SSL3_TXT_RSA_DES_192_CBC3_SHA
+    CIPHER(0x000a, SSL3_TXT_RSA_DES_192_CBC3_SHA)
+#else
+   XCIPHER(0x000a, SSL3_TXT_RSA_DES_192_CBC3_SHA)
+#endif


Property changes on: tor/trunk/src/common/ciphers.inc
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: tor/trunk/src/common/tortls.c
===================================================================
--- tor/trunk/src/common/tortls.c	2008-06-12 21:36:09 UTC (rev 15172)
+++ tor/trunk/src/common/tortls.c	2008-06-12 22:39:13 UTC (rev 15173)
@@ -101,6 +101,16 @@
   void *callback_arg;
 };
 
+#ifdef V2_HANDSHAKE_CLIENT
+/** An array of fake SSL_CIPHER objects that we use in order to trick OpenSSL
+ * in client mode into advertising the ciphers we want.  See
+ * rectify_client_ciphers for details. */
+static SSL_CIPHER *CLIENT_CIPHER_DUMMIES = NULL;
+/** A stack of SSL_CIPHER objects, some real, some fake.
+ * See rectify_client_ciphers for details. */
+static STACK_OF(SSL_CIPHER) *CLIENT_CIPHER_STACK = NULL;
+#endif
+
 /** Helper: compare tor_tls_t objects by its SSL. */
 static INLINE int
 tor_tls_entries_eq(const tor_tls_t *a, const tor_tls_t *b)
@@ -318,6 +328,12 @@
     log_warn(LD_MM, "Still have entries in the tlsmap at shutdown.");
   }
   HT_CLEAR(tlsmap, &tlsmap_root);
+#ifdef V2_HANDSHAKE_CLIENT
+  if (CLIENT_CIPHER_DUMMIES)
+    tor_free(CLIENT_CIPHER_DUMMIES);
+  if (CLIENT_CIPHER_STACK)
+    sk_SSL_CIPHER_free(CLIENT_CIPHER_STACK);
+#endif
 }
 
 /** We need to give OpenSSL a callback to verify certificates. This is
@@ -427,58 +443,42 @@
   return x509;
 }
 
-#define SERVER_CIPHER_LIST                      \
-  (TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":"        \
-   TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":"        \
+/** List of ciphers that servers should select from.*/
+#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)
 /* Note: for setting up your own private testing network with link crypto
  * disabled, set the cipher lists to your cipher list to
  * SSL3_TXT_RSA_NULL_SHA.  If you do this, you won't be able to communicate
  * with any of the "real" Tors, though. */
 
-#if OPENSSL_VERSION_NUMBER >= 0x00908020l
-#define CLIENT_CIPHER_LIST                         \
-  (TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ":"   \
-   TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA ":"     \
-   TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":"           \
-   TLS1_TXT_DHE_DSS_WITH_AES_256_SHA ":"           \
-   TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA ":"      \
-   TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA ":"    \
-   TLS1_TXT_RSA_WITH_AES_256_SHA ":"               \
-   TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA ":"       \
-   TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA ":"   \
-   TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA ":"         \
-   TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA ":"     \
-   TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":"           \
-   TLS1_TXT_DHE_DSS_WITH_AES_128_SHA ":"           \
-   TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA":"           \
-   TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA":"       \
-   TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA  ":"       \
-   TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA ":"    \
-   SSL3_TXT_RSA_RC4_128_MD5 ":"                    \
-   SSL3_TXT_RSA_RC4_128_SHA ":"                    \
-   TLS1_TXT_RSA_WITH_AES_128_SHA ":"               \
-   TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA ":"  \
-   TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA ":"    \
-   SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA ":"           \
-   SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA ":"           \
-   TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA ":"     \
-   TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA ":"   \
-   /*SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA ":"*/ \
-   SSL3_TXT_RSA_DES_192_CBC3_SHA)
-/* SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA is commented out because it doesn't
- * really exist; if I understand correctly, it's a bit of silliness that
- * netscape did on its own before any standard for what they wanted was
- * formally approved.  Nonetheless, Firefox still uses it, so we need to
- * fake it at some point soon. XXXX021 -NM */
-#else
-/* Ug. We don't have as many ciphers with openssl 0.9.7 as we'd like.  Fix
- * this list into something that sucks less. */
-#define CLIENT_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 ":"        \
-   SSL3_TXT_RSA_RC4_128_SHA)
+#ifdef V2_HANDSHAKE_CLIENT
+#define CIPHER(id, name) name ":"
+#define XCIPHER(id, name)
+/** List of ciphers that clients should advertise, omitting items that
+ * our openssl doesn't know about. */
+static const char CLIENT_CIPHER_LIST[] =
+#include "./ciphers.inc"
+  ;
+#undef CIPHER
+#undef XCIPHER
+
+/** Holds a cipher that we want to advertise, and its 2-byte ID. */
+typedef struct cipher_info_t { unsigned id; const char *name; } cipher_info_t;
+/** A list of all the ciphers that clients should advertise, including items
+ * that openssl might not know about. */
+static const cipher_info_t CLIENT_CIPHER_INFO_LIST[] = {
+#define CIPHER(id, name) { id, name },
+#define XCIPHER(id, name) { id, #name },
+#include "./ciphers.inc"
+#undef CIPHER
+#undef XCIPHER
+};
+
+/** The length of CLIENT_CIPHER_INFO_LIST and CLIENT_CIPHER_DUMMIES. */
+static const int N_CLIENT_CIPHERS =
+  sizeof(CLIENT_CIPHER_INFO_LIST)/sizeof(CLIENT_CIPHER_INFO_LIST[0]);
 #endif
 
 #ifndef V2_HANDSHAKE_CLIENT
@@ -730,6 +730,76 @@
 }
 #endif
 
+/** Replace *<b>ciphers</b> with a new list of SSL ciphersuites: specifically,
+ * a list designed to mimic a common web browser.  Some of the cipher in the
+ * list won't actually be implemented by OpenSSL: that's okay so long as the
+ * server doesn't select them, and the server won't select anything besides
+ * what's in SERVER_CIPHER_LIST.
+ *
+ * [If the server <b>does</b> select a bogus cipher, we won't crash or
+ * anything; we'll just fail later when we try to look up the cipher in
+ * ssl->cipher_list_by_id.]
+ */
+static void
+rectify_client_ciphers(STACK_OF(SSL_CIPHER) **ciphers)
+{
+#ifdef V2_HANDSHAKE_CLIENT
+  if (PREDICT_UNLIKELY(!CLIENT_CIPHER_STACK)) {
+    /* We need to set CLIENT_CIPHER_STACK to an array of the ciphers
+     * we want.*/
+    int i = 0, j = 0;
+
+    /* First, create a dummy SSL_CIPHER for every cipher. */
+    CLIENT_CIPHER_DUMMIES =
+      tor_malloc_zero(sizeof(SSL_CIPHER)*N_CLIENT_CIPHERS);
+    for (i=0; i < N_CLIENT_CIPHERS; ++i) {
+      CLIENT_CIPHER_DUMMIES[i].valid = 1;
+      CLIENT_CIPHER_DUMMIES[i].id = CLIENT_CIPHER_INFO_LIST[i].id | (3<<24);
+      CLIENT_CIPHER_DUMMIES[i].name = CLIENT_CIPHER_INFO_LIST[i].name;
+    }
+
+    CLIENT_CIPHER_STACK = sk_SSL_CIPHER_new_null();
+    tor_assert(CLIENT_CIPHER_STACK);
+
+    log_debug(LD_NET, "List was: %s", CLIENT_CIPHER_LIST);
+    for (j = 0; j < sk_SSL_CIPHER_num(*ciphers); ++j) {
+      SSL_CIPHER *cipher = sk_SSL_CIPHER_value(*ciphers, j);
+      log_debug(LD_NET, "Cipher %d: %lx %s", j, cipher->id, cipher->name);
+    }
+
+    /* Then copy as many ciphers as we can from the good list, inserting
+     * dummies as needed. */
+    j=0;
+    for (i = 0; i < N_CLIENT_CIPHERS; ) {
+      SSL_CIPHER *cipher = NULL;
+      if (j < sk_SSL_CIPHER_num(*ciphers))
+        cipher = sk_SSL_CIPHER_value(*ciphers, j);
+      if (cipher && ((cipher->id >> 24) & 0xff) != 3) {
+        log_debug(LD_NET, "Skipping v2 cipher %s", cipher->name);
+        ++j;
+      } else if (cipher &&
+                 (cipher->id & 0xffff) == CLIENT_CIPHER_INFO_LIST[i].id) {
+        log_debug(LD_NET, "Found cipher %s", cipher->name);
+        sk_SSL_CIPHER_push(CLIENT_CIPHER_STACK, cipher);
+        ++j;
+        ++i;
+      } else {
+        log_debug(LD_NET, "Inserting fake %s", CLIENT_CIPHER_DUMMIES[i].name);
+        sk_SSL_CIPHER_push(CLIENT_CIPHER_STACK, &CLIENT_CIPHER_DUMMIES[i]);
+        ++i;
+      }
+    }
+  }
+
+  sk_SSL_CIPHER_free(*ciphers);
+  *ciphers = sk_SSL_CIPHER_dup(CLIENT_CIPHER_STACK);
+  tor_assert(*ciphers);
+
+#else
+    (void)ciphers;
+#endif
+}
+
 /** Create a new TLS object from a file descriptor, and a flag to
  * determine whether it is functioning as a server.
  */
@@ -745,12 +815,25 @@
     tor_free(result);
     return NULL;
   }
+
+#ifdef SSL_set_tlsext_host_name
+  /* Browsers use the TLS hostname extension, so we should too. */
+  {
+    char *fake_hostname = crypto_random_hostname(4,25, "www.",".com");
+    SSL_set_tlsext_host_name(result->ssl, fake_hostname);
+  }
+#endif
+
   if (!SSL_set_cipher_list(result->ssl,
                      isServer ? SERVER_CIPHER_LIST : CLIENT_CIPHER_LIST)) {
+    tls_log_errors(NULL, LOG_WARN, "setting ciphers");
+    log_warn(LD_NET, "WTF?");
     SSL_free(result->ssl);
     tor_free(result);
     return NULL;
   }
+  if (!isServer)
+    rectify_client_ciphers(&result->ssl->cipher_list);
   result->socket = sock;
 #ifdef USE_BSOCKETS
   bio = BIO_new_bsocket(sock, BIO_NOCLOSE);
@@ -982,7 +1065,10 @@
       if (cert)
         X509_free(cert);
 #endif
-      SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST);
+      if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) {
+        tls_log_errors(NULL, LOG_WARN, "re-setting ciphers");
+        r = TOR_TLS_ERROR_MISC;
+      }
     }
   }
   return r;