From d00dc9f7d1b98dd80bbc890db1ddf497aae3bb9e Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 8 Apr 2014 03:19:38 -0400 Subject: [PATCH 1/9] Teach the get_mozilla_ciphers.py script to parse recent firefoxen --- src/common/get_mozilla_ciphers.py | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/common/get_mozilla_ciphers.py b/src/common/get_mozilla_ciphers.py index c7e9a84a0..0636eb365 100644 --- a/src/common/get_mozilla_ciphers.py +++ b/src/common/get_mozilla_ciphers.py @@ -41,12 +41,12 @@ fileA = open(ff('security/manager/ssl/src/nsNSSComponent.cpp'),'r') inCipherSection = False cipherLines = [] for line in fileA: - if line.startswith('static CipherPref CipherPrefs'): + if line.startswith('static const CipherPref sCipherPrefs[]'): # Get the starting boundary of the Cipher Preferences inCipherSection = True elif inCipherSection: line = line.strip() - if line.startswith('{NULL, 0}'): + if line.startswith('{ nullptr, 0}'): # At the ending boundary of the Cipher Prefs break else: @@ -56,12 +56,30 @@ fileA.close() # Parse the lines and put them into a dict ciphers = {} cipher_pref = {} +key_pending = None for line in cipherLines: - m = re.search(r'^{\s*\"([^\"]+)\",\s*(\S*)\s*}', line) + m = re.search(r'^{\s*\"([^\"]+)\",\s*(\S+)\s*(?:,\s*(true|false))?\s*}', line) if m: - key,value = m.groups() - ciphers[key] = value - cipher_pref[value] = key + assert not key_pending + key,value,enabled = m.groups() + if enabled == 'true': + ciphers[key] = value + cipher_pref[value] = key + continue + m = re.search(r'^{\s*\"([^\"]+)\",', line) + if m: + assert not key_pending + key_pending = m.group(1) + continue + m = re.search(r'^\s*(\S+)(?:,\s*(true|false))?\s*}', line) + if m: + assert key_pending + key = key_pending + value,enabled = m.groups() + key_pending = None + if enabled == 'true': + ciphers[key] = value + cipher_pref[value] = key #### # Now find the correct order for the ciphers From 4231729176c63d28f7adb61074f79464e2ee73a7 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 8 Apr 2014 11:31:48 -0400 Subject: [PATCH 2/9] Update ciphers.inc to match ff28 The major changes are to re-order some ciphers, to drop the ECDH suites (note: *not* ECDHE: ECDHE is still there), to kill off some made-up stuff (like the SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA suite), to drop some of the DSS suites... *and* to enable the ECDHE+GCM ciphersuites. This change is autogenerated by get_mozilla_ciphers.py from Firefox 28 and OpenSSL 1.0.1g. Resolves ticket 11438. --- changes/ff28_ciphers | 6 ++ src/common/ciphers.inc | 181 ++++++++++++++--------------------------- 2 files changed, 66 insertions(+), 121 deletions(-) create mode 100644 changes/ff28_ciphers diff --git a/changes/ff28_ciphers b/changes/ff28_ciphers new file mode 100644 index 000000000..05eb4e9bc --- /dev/null +++ b/changes/ff28_ciphers @@ -0,0 +1,6 @@ + o Minor features (performance, compatibility): + - Update the list of TLS cipehrsuites that a client advertises + to match those advertised by Firefox 28. This enables selection of + (fast) GCM ciphersuites, disables some strange old ciphers, and + disables the ECDH (not to be confused with ECDHE) ciphersuites. + Resolves ticket 11438. diff --git a/src/common/ciphers.inc b/src/common/ciphers.inc index 137d78b11..ab4ac4072 100644 --- a/src/common/ciphers.inc +++ b/src/common/ciphers.inc @@ -4,85 +4,50 @@ * * This file was automatically generated by get_mozilla_ciphers.py. */ +#ifdef TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + CIPHER(0xc02b, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) +#else + XCIPHER(0xc02b, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) +#endif +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + CIPHER(0xc02f, TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256) +#else + XCIPHER(0xc02f, TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256) +#endif #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_CAMELLIA_256_CBC_SHA - CIPHER(0x0088, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA) -#else - XCIPHER(0x0088, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA) -#endif -#ifdef TLS1_TXT_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA - CIPHER(0x0087, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA) -#else - XCIPHER(0x0087, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_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_CAMELLIA_256_CBC_SHA - CIPHER(0x0084, TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA) -#else - XCIPHER(0x0084, TLS1_TXT_RSA_WITH_CAMELLIA_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_CAMELLIA_128_CBC_SHA - CIPHER(0x0045, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA) +#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA + CIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA) #else - XCIPHER(0x0045, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA) + XCIPHER(0xc014, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA) #endif -#ifdef TLS1_TXT_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA - CIPHER(0x0044, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA) +#ifdef TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA + CIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA) #else - XCIPHER(0x0044, TLS1_TXT_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA) + XCIPHER(0xc012, TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_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_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_DHE_RSA_WITH_AES_128_SHA CIPHER(0x0033, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) @@ -94,89 +59,63 @@ #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) +#ifdef TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + CIPHER(0x0045, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA) #else - XCIPHER(0xc00c, TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA) + XCIPHER(0x0045, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA) #endif -#ifdef TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA - CIPHER(0xc00e, TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA) +#ifdef TLS1_TXT_DHE_RSA_WITH_AES_256_SHA + CIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) #else - XCIPHER(0xc00e, TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA) + XCIPHER(0x0039, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) #endif -#ifdef TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA - CIPHER(0xc002, TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA) +#ifdef TLS1_TXT_DHE_DSS_WITH_AES_256_SHA + CIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA) #else - XCIPHER(0xc002, TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA) + XCIPHER(0x0038, TLS1_TXT_DHE_DSS_WITH_AES_256_SHA) #endif -#ifdef TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA - CIPHER(0xc004, TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA) +#ifdef TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + CIPHER(0x0088, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA) #else - XCIPHER(0xc004, TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA) -#endif -#ifdef TLS1_TXT_RSA_WITH_SEED_SHA - CIPHER(0x0096, TLS1_TXT_RSA_WITH_SEED_SHA) -#else - XCIPHER(0x0096, TLS1_TXT_RSA_WITH_SEED_SHA) -#endif -#ifdef TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA - CIPHER(0x0041, TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA) -#else - XCIPHER(0x0041, TLS1_TXT_RSA_WITH_CAMELLIA_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) + XCIPHER(0x0088, TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_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) +#ifdef TLS1_TXT_RSA_WITH_AES_128_SHA + CIPHER(0x002f, TLS1_TXT_RSA_WITH_AES_128_SHA) #else - XCIPHER(0x0013, SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA) + XCIPHER(0x002f, TLS1_TXT_RSA_WITH_AES_128_SHA) #endif -#ifdef TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA - CIPHER(0xc00d, TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA) +#ifdef TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA + CIPHER(0x0041, TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA) #else - XCIPHER(0xc00d, TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA) + XCIPHER(0x0041, TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA) #endif -#ifdef TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA - CIPHER(0xc003, TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA) +#ifdef TLS1_TXT_RSA_WITH_AES_256_SHA + CIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA) #else - XCIPHER(0xc003, TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA) + XCIPHER(0x0035, TLS1_TXT_RSA_WITH_AES_256_SHA) #endif -/* No openssl macro found for 0xfeff */ -#ifdef SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA - CIPHER(0xfeff, SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA) +#ifdef TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA + CIPHER(0x0084, TLS1_TXT_RSA_WITH_CAMELLIA_256_CBC_SHA) #else - XCIPHER(0xfeff, SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA) + XCIPHER(0x0084, TLS1_TXT_RSA_WITH_CAMELLIA_256_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 +#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 SSL3_TXT_RSA_RC4_128_MD5 + CIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5) +#else + XCIPHER(0x0004, SSL3_TXT_RSA_RC4_128_MD5) +#endif From bd3db82906a2efcd678b5f4b61fef26c93828777 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 14 Apr 2014 14:10:05 -0400 Subject: [PATCH 3/9] 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(-) create mode 100644 changes/bug11513 create mode 100755 src/common/gen_server_ciphers.py diff --git a/changes/bug11513 b/changes/bug11513 new file mode 100644 index 000000000..820c02605 --- /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 000000000..97ed9d046 --- /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 886ee0dda..bbbf6a5ef 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 -//#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_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 + /* 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 From 0b319de60f7c80ab5c37c57af182f4f710ceb5b7 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 17 Apr 2014 10:23:18 -0400 Subject: [PATCH 4/9] Elevate server TLS cipher preferences over client The server cipher list is (thanks to #11513) chosen systematically to put the best choices for Tor first. The client cipher list is chosen to resemble a browser. So let's set SSL_OP_CIPHER_SERVER_PREFERENCE to have the servers pick according to their own preference order. --- changes/ticket11528 | 6 ++++++ src/common/tortls.c | 4 ++++ 2 files changed, 10 insertions(+) create mode 100644 changes/ticket11528 diff --git a/changes/ticket11528 b/changes/ticket11528 new file mode 100644 index 000000000..15daad995 --- /dev/null +++ b/changes/ticket11528 @@ -0,0 +1,6 @@ + o Minor features: + - Servers now trust themselves to have a better view than clients of + which TLS ciphersuites to choose. (Thanks to #11513, the server + list is now well-considered, whereas the client list has been + chosen mainly for anti-fingerprinting purposes.) Resolves ticket + 11528. diff --git a/src/common/tortls.c b/src/common/tortls.c index 886ee0dda..8eb524ebf 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -1261,6 +1261,10 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, goto error; SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2); + /* Prefer the server's ordering of ciphers: the client's ordering has + * historically been chosen for fingerprinting resistance. */ + SSL_CTX_set_options(result->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + /* Disable TLS1.1 and TLS1.2 if they exist. We need to do this to * workaround a bug present in all OpenSSL 1.0.1 versions (as of 1 * June 2012), wherein renegotiating while using one of these TLS From bb9b4c37f8e7f5cf78918f382e90d8b11ff42551 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 18 Apr 2014 12:28:30 -0400 Subject: [PATCH 5/9] Supply better and less frequent warnings on circID exhaustion Fixes the surface behavior of #11553 --- changes/bug11553 | 5 +++++ src/or/channel.h | 2 ++ src/or/circuitbuild.c | 9 ++++++++- 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 changes/bug11553 diff --git a/changes/bug11553 b/changes/bug11553 new file mode 100644 index 000000000..1540f4642 --- /dev/null +++ b/changes/bug11553 @@ -0,0 +1,5 @@ + o Minor features: + - When we run out of usable circuit IDs on a channel, log only one + warning for the whole channel, and include a description of + how many circuits there were on the channel. Fix for part of ticket + #11553. diff --git a/src/or/channel.h b/src/or/channel.h index 2dca81705..29ba40e32 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -148,6 +148,8 @@ struct channel_s { ENUM_BF(circ_id_type_t) circ_id_type:2; /** DOCDOC*/ unsigned wide_circ_ids:1; + /** Have we logged a warning about circID exhaustion on this channel? */ + unsigned warned_circ_ids_exhausted:1; /* * Which circ_id do we try to use next on this connection? This is * always in the range 0..1<<15-1. diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index e47a2780a..2b4d3c311 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -127,7 +127,14 @@ get_unique_circ_id_by_chan(channel_t *chan) /* Make sure we don't loop forever if all circ_id's are used. This * matters because it's an external DoS opportunity. */ - log_warn(LD_CIRC,"No unused circ IDs. Failing."); + if (! chan->warned_circ_ids_exhausted) { + chan->warned_circ_ids_exhausted = 1; + log_warn(LD_CIRC,"No unused circIDs found on channel %s wide " + "circID support, with %u inbound and %u outbound circuits. " + "Failing a circuit.", + chan->wide_circ_ids ? "with" : "without", + chan->num_p_circuits, chan->num_n_circuits); + } return 0; } test_circ_id |= high_bit; From f8248abbd6c5e04037cedee29a8867700617acde Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 25 Apr 2014 14:24:41 -0400 Subject: [PATCH 6/9] Forbid TunneledDirConns 0 and PreferTunneledDirConns 0 if being a HS Fixes bug 10849; bugfix on 0.2.1.1-alpha (I believe) --- changes/bug10849_023 | 6 ++++++ src/or/config.c | 4 ++++ 2 files changed, 10 insertions(+) create mode 100644 changes/bug10849_023 diff --git a/changes/bug10849_023 b/changes/bug10849_023 new file mode 100644 index 000000000..480dea3de --- /dev/null +++ b/changes/bug10849_023 @@ -0,0 +1,6 @@ + o Major bugfixes: + - When running a hidden service, do not allow TunneledDirConns 0; + this will keep the hidden service from running, and also + make it publish its descriptors directly over HTTP. Fixes bug 10849; + bugfix on 0.2.1.1-alpha. + diff --git a/src/or/config.c b/src/or/config.c index d5c568947..f6db98e66 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -4049,6 +4049,10 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("If you set UseBridges, you must specify at least one bridge."); if (options->UseBridges && !options->TunnelDirConns) REJECT("If you set UseBridges, you must set TunnelDirConns."); + if (options->RendConfigLines && + (!options->TunnelDirConns || !options->PreferTunneledDirConns)) + REJECT("If you are running a hidden service, you must set TunnelDirConns " + "and PreferTunneledDirConns"); for (cl = options->Bridges; cl; cl = cl->next) { if (parse_bridge_line(cl->value, 1)<0) From 65575b0755f64d21d59532bf58e6c27e14086bbb Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sat, 26 Apr 2014 12:45:34 -0400 Subject: [PATCH 7/9] Stop leaking memory in error cases of md parsing When clearing a list of tokens, it's important to do token_clear() on them first, or else any keys they contain will leak. This didn't leak memory on any of the successful microdescriptor parsing paths, but it does leak on some failing paths when the failure happens during tokenization. Fixes bug 11618; bugfix on 0.2.2.6-alpha. --- changes/md_leak_bug | 5 +++++ src/or/routerparse.c | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 changes/md_leak_bug diff --git a/changes/md_leak_bug b/changes/md_leak_bug new file mode 100644 index 000000000..26270aacc --- /dev/null +++ b/changes/md_leak_bug @@ -0,0 +1,5 @@ + o Major bugfixes (security, OOM) + - Fix a memory leak that could occur if a microdescriptor parse + fails during the tokenizing step. This could enable a memory + exhaustion attack by directory servers. Fixes bug #11649; bugfix + on 0.2.2.6-alpha. diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 97e0bc8c8..3ff887c3c 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -4455,11 +4455,13 @@ microdescs_parse_from_string(const char *s, const char *eos, microdesc_free(md); md = NULL; + SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); memarea_clear(area); smartlist_clear(tokens); s = start_of_next_microdesc; } + SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); memarea_drop_all(area); smartlist_free(tokens); From 35699ef9f5d2814203653e16cb0cd176a0190ae0 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 20 Nov 2013 12:12:47 -0500 Subject: [PATCH 8/9] Drop the MaxMemInCellQueues lower limit down to 256 MB. on #9686, gmorehose reports that the 500 MB lower limit is too high for raspberry pi users. This is a backport of 647248729fa65f0e51d062e2af8f4e8b38592bf5 to 0.2.4. Note that in 0.2.4, the option is called MaxMemInCellQueues. --- changes/bug9686_024 | 5 +++++ src/or/config.c | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 changes/bug9686_024 diff --git a/changes/bug9686_024 b/changes/bug9686_024 new file mode 100644 index 000000000..8705379d3 --- /dev/null +++ b/changes/bug9686_024 @@ -0,0 +1,5 @@ + o Minor features (security): + - Decrease the lower limit of MaxMemInCellQueues to 256 MBytes (but leave + the default at 8GBytes), to better support Raspberry Pi users. Fixes + bug 9686; bugfix on 0.2.4.14-alpha. + diff --git a/src/or/config.c b/src/or/config.c index ef0294626..85a5e83d5 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -2616,10 +2616,10 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("If EntryNodes is set, UseEntryGuards must be enabled."); } - if (options->MaxMemInCellQueues < (500 << 20)) { - log_warn(LD_CONFIG, "MaxMemInCellQueues must be at least 500 MB for now. " + if (options->MaxMemInCellQueues < (256 << 20)) { + log_warn(LD_CONFIG, "MaxMemInCellQueues must be at least 256 MB for now. " "Ideally, have it as large as you can afford."); - options->MaxMemInCellQueues = (500 << 20); + options->MaxMemInCellQueues = (256 << 20); } options->AllowInvalid_ = 0; From 6a4f5d9b4df1d05d1b158417cfcc85a547ad1549 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 1 May 2014 11:42:02 -0400 Subject: [PATCH 9/9] Downgrade bug 7164 warning to INFO The 0.2.5.x warning is the one that might help us track this down; the warnings in stable are just annoying users over and over and over. --- changes/bug7164_downgrade | 6 ++++++ src/or/microdesc.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 changes/bug7164_downgrade diff --git a/changes/bug7164_downgrade b/changes/bug7164_downgrade new file mode 100644 index 000000000..4d75586bb --- /dev/null +++ b/changes/bug7164_downgrade @@ -0,0 +1,6 @@ + o Minor bugfixes: + - Downgrade the warning severity for the the "md was still referenced 1 + node(s)" warning. Tor 0.2.5.4-alpha has better code for trying to + diagnose this bug, and the current warning in earlier versions of + tor achieves nothing useful. Addresses warning from bug 7164. + diff --git a/src/or/microdesc.c b/src/or/microdesc.c index b4d22c1c6..4acec6ae3 100644 --- a/src/or/microdesc.c +++ b/src/or/microdesc.c @@ -561,7 +561,7 @@ microdesc_free(microdesc_t *md) } }); if (found) { - log_warn(LD_BUG, "microdesc_free() called, but md was still referenced " + log_info(LD_BUG, "microdesc_free() called, but md was still referenced " "%d node(s); held_by_nodes == %u", found, md->held_by_nodes); } else { log_warn(LD_BUG, "microdesc_free() called with held_by_nodes set to %u, "