[tor-commits] [tor/master] New sort order for server choice of ciphersuites.

nickm at torproject.org nickm at torproject.org
Tue Apr 15 19:05:08 UTC 2014


commit bd3db82906a2efcd678b5f4b61fef26c93828777
Author: Nick Mathewson <nickm at torproject.org>
Date:   Mon Apr 14 14:10:05 2014 -0400

    New sort order for server choice of ciphersuites.
    
    Back in 175b2678, we allowed servers to recognize clients who are
    telling them the truth about their ciphersuites, and select the best
    cipher from on that list. This implemented the server side of proposal
    198.
    
    In bugs 11492, 11498, and 11499, cypherpunks found a bunch of mistakes
    and omissions and typos in the UNRESTRICTED_SERVER_CIPHER_LIST we had.
    In #11513, I found a couple more.
    
    Rather than try to hand-edit this list, I wrote a short python script
    to generate our ciphersuite preferences from the openssl headers.
    
    The new rules are:
      * Require forward secrecy.
      * Require RSA (since our servers only configure RSA keys)
      * Require AES or 3DES. (This means, reject RC4, DES, SEED, CAMELLIA,
        and NULL.)
      * No export ciphersuites.
    
    Then:
      * Prefer AES to 3DES.
      * If both suites have the same cipher, prefer ECDHE to DHE.
      * If both suites have the same DHE group type, prefer GCM to CBC.
      * If both suites have the same cipher mode, prefer SHA384 to SHA256
        to SHA1.
      * If both suites have the same digest, prefer AES256 to AES128.
---
 changes/bug11513                 |   12 ++++
 src/common/gen_server_ciphers.py |  115 ++++++++++++++++++++++++++++++++++++++
 src/common/tortls.c              |   40 +++++++++----
 3 files changed, 155 insertions(+), 12 deletions(-)

diff --git a/changes/bug11513 b/changes/bug11513
new file mode 100644
index 0000000..820c026
--- /dev/null
+++ b/changes/bug11513
@@ -0,0 +1,12 @@
+  o Major bugfixes:
+    - Generate the server's preference list for ciphersuites
+      automatically based on uniform criteria, and considering all
+      OpenSSL ciphersuites with acceptable strength and forward
+      secrecy. (The sort order is: prefer AES to 3DES; break ties by
+      preferring ECDHE to DHE; break ties by preferring GCM to CBC;
+      break ties by preferring SHA384 to SHA256 to SHA1; and finally,
+      break ties by preferring AES256 to AES128.) This resolves bugs
+      #11513, #11492, #11498, #11499. Bugs reported by 'cypherpunks'.
+      Bugfix on 0.2.4.8-alpha.
+
+
diff --git a/src/common/gen_server_ciphers.py b/src/common/gen_server_ciphers.py
new file mode 100755
index 0000000..97ed9d0
--- /dev/null
+++ b/src/common/gen_server_ciphers.py
@@ -0,0 +1,115 @@
+#!/usr/bin/python
+# Copyright 2014, The Tor Project, Inc
+# See LICENSE for licensing information
+
+# This script parses openssl headers to find ciphersuite names, determines
+# which ones we should be willing to use as a server, and sorts them according
+# to preference rules.
+#
+# Run it on all the files in your openssl include directory.
+
+import re
+import sys
+
+EPHEMERAL_INDICATORS = [ "_EDH_", "_DHE_", "_ECDHE_" ]
+BAD_STUFF = [ "_DES_40_", "MD5", "_RC4_", "_DES_64_",
+              "_SEED_", "_CAMELLIA_", "_NULL" ]
+
+# these never get #ifdeffed.
+MANDATORY = [
+    "TLS1_TXT_DHE_RSA_WITH_AES_256_SHA",
+    "TLS1_TXT_DHE_RSA_WITH_AES_128_SHA",
+    "SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA",
+]
+
+def find_ciphers(filename):
+    with open(filename) as f:
+        for line in f:
+            m = re.search(r'(?:SSL3|TLS1)_TXT_\w+', line)
+            if m:
+                yield m.group(0)
+
+def usable_cipher(ciph):
+    ephemeral = False
+    for e in EPHEMERAL_INDICATORS:
+        if e in ciph:
+            ephemeral = True
+    if not ephemeral:
+        return False
+
+    if "_RSA_" not in ciph:
+        return False
+
+    for b in BAD_STUFF:
+        if b in ciph:
+            return False
+    return True
+
+# All fields we sort on, in order of priority.
+FIELDS = [ 'cipher', 'fwsec', 'mode',  'digest', 'bitlength' ]
+# Map from sorted fields to recognized value in descending order of goodness
+FIELD_VALS = { 'cipher' : [ 'AES', 'DES'],
+               'fwsec' : [ 'ECDHE', 'DHE' ],
+               'mode' : [ 'GCM', 'CBC' ],
+               'digest' : [ 'SHA384', 'SHA256', 'SHA' ],
+               'bitlength' : [ '256', '128', '192' ],
+}
+
+class Ciphersuite(object):
+    def __init__(self, name, fwsec, cipher, bitlength, mode, digest):
+        self.name = name
+        self.fwsec = fwsec
+        self.cipher = cipher
+        self.bitlength = bitlength
+        self.mode = mode
+        self.digest = digest
+
+        for f in FIELDS:
+            assert(getattr(self, f) in FIELD_VALS[f])
+
+    def sort_key(self):
+        return tuple(FIELD_VALS[f].index(getattr(self,f)) for f in FIELDS)
+
+
+def parse_cipher(ciph):
+    m = re.match('(?:TLS1|SSL3)_TXT_(EDH|DHE|ECDHE)_RSA(?:_WITH)?_(AES|DES)_(256|128|192)(|_CBC|_CBC3|_GCM)_(SHA|SHA256|SHA384)$', ciph)
+
+    if not m:
+        print "/* Couldn't parse %s ! */"%ciph
+        return None
+
+    fwsec, cipher, bits, mode, digest = m.groups()
+    if fwsec == 'EDH':
+        fwsec = 'DHE'
+
+    if mode in [ '_CBC3', '_CBC', '' ]:
+        mode = 'CBC'
+    elif mode == '_GCM':
+        mode = 'GCM'
+
+    return Ciphersuite(ciph, fwsec, cipher, bits, mode, digest)
+
+ALL_CIPHERS = []
+
+for fname in sys.argv[1:]:
+    ALL_CIPHERS += (parse_cipher(c)
+                           for c in find_ciphers(fname)
+                           if usable_cipher(c) )
+
+ALL_CIPHERS.sort(key=Ciphersuite.sort_key)
+
+for c in ALL_CIPHERS:
+    if c is ALL_CIPHERS[-1]:
+        colon = ';'
+    else:
+        colon = ' ":"'
+
+    if c.name in MANDATORY:
+        print "       /* Required */"
+        print '       %s%s'%(c.name,colon)
+    else:
+        print "#ifdef %s"%c.name
+        print '       %s%s'%(c.name,colon)
+        print "#endif"
+
+
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 886ee0d..bbbf6a5 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -714,31 +714,47 @@ tor_tls_create_certificate(crypto_pk_t *rsa,
 /** 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
+  /* This list is autogenerated with the gen_server_ciphers.py script;
+   * don't hand-edit it. */
 #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_GCM_SHA256
+       TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ":"
+#endif
+#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384
+       TLS1_TXT_ECDHE_RSA_WITH_AES_256_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_256_CBC_SHA
+       TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA ":"
+#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
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384
+       TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384 ":"
+#endif
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256
+       TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256 ":"
+#endif
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256
+       TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256 ":"
+#endif
+#ifdef TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256
+       TLS1_TXT_DHE_RSA_WITH_AES_128_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 ":"
+       /* Required */
+       TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":"
+       /* Required */
+       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;
+       /* Required */
+       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





More information about the tor-commits mailing list