mirror of
https://github.com/mozilla/cipherscan.git
synced 2024-11-22 14:23:41 +01:00
make cipher selection simulation generic
it's relatively easy to make the cipher selection generic, so that adding different clients is as easy as converting their client hello cipher ordering to openssl cipher names
This commit is contained in:
parent
c82bc44558
commit
76d791fcbe
@ -21,8 +21,10 @@ def natural_sort(l):
|
|||||||
alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ]
|
alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ]
|
||||||
return sorted(l, key = alphanum_key)
|
return sorted(l, key = alphanum_key)
|
||||||
|
|
||||||
""" list of ciphers offerred by Firefox 29 by default """
|
""" client config cipher simulation """
|
||||||
firefox_ciphers=[
|
client_ciphers={}
|
||||||
|
""" list of ciphers offered by Firefox 29 by default """
|
||||||
|
client_ciphers['FF 29']=[
|
||||||
'ECDHE-ECDSA-AES128-GCM-SHA256',
|
'ECDHE-ECDSA-AES128-GCM-SHA256',
|
||||||
'ECDHE-RSA-AES128-GCM-SHA256',
|
'ECDHE-RSA-AES128-GCM-SHA256',
|
||||||
'ECDHE-ECDSA-AES256-SHA',
|
'ECDHE-ECDSA-AES256-SHA',
|
||||||
@ -50,10 +52,19 @@ firefox_ciphers=[
|
|||||||
report_untrused=False
|
report_untrused=False
|
||||||
|
|
||||||
cipherstats = defaultdict(int)
|
cipherstats = defaultdict(int)
|
||||||
FF_RC4_Only_cipherstats = defaultdict(int)
|
|
||||||
FF_RC4_preferred_cipherstats = defaultdict(int)
|
# stats about different client performance
|
||||||
FF_incompatible_cipherstats = defaultdict(int)
|
# ciphers selected by them, unsupported, etc.
|
||||||
FF_selected_cipherstats = defaultdict(int)
|
client_RC4_Only_cipherstats={}
|
||||||
|
client_RC4_preferred_cipherstats={}
|
||||||
|
client_incompatible_cipherstats={}
|
||||||
|
client_selected_cipherstats={}
|
||||||
|
for client_name in client_ciphers:
|
||||||
|
client_RC4_Only_cipherstats[client_name] = defaultdict(int)
|
||||||
|
client_RC4_preferred_cipherstats[client_name] = defaultdict(int)
|
||||||
|
client_incompatible_cipherstats[client_name] = defaultdict(int)
|
||||||
|
client_selected_cipherstats[client_name] = defaultdict(int)
|
||||||
|
|
||||||
cipherordering = defaultdict(int)
|
cipherordering = defaultdict(int)
|
||||||
pfsstats = defaultdict(int)
|
pfsstats = defaultdict(int)
|
||||||
protocolstats = defaultdict(int)
|
protocolstats = defaultdict(int)
|
||||||
@ -84,13 +95,22 @@ for r,d,flist in os.walk(path):
|
|||||||
DES3 = False
|
DES3 = False
|
||||||
CAMELLIA = False
|
CAMELLIA = False
|
||||||
RC4 = False
|
RC4 = False
|
||||||
""" the following depends on FF_compat, so by default it can be True """
|
""" variables to support handshake simulation for different clients """
|
||||||
RC4_Only_FF = True
|
client_RC4_Only={}
|
||||||
FF_compat = False
|
client_compat={}
|
||||||
temp_FF_incompat = {}
|
temp_client_incompat={}
|
||||||
|
client_RC4_Pref={}
|
||||||
|
client_selected={}
|
||||||
|
for client_name in client_ciphers:
|
||||||
|
# the following depends on client_compat, so by default it can be True
|
||||||
|
client_RC4_Only[client_name]=True
|
||||||
|
client_compat[client_name]=False
|
||||||
|
temp_client_incompat[client_name]={}
|
||||||
|
client_RC4_Pref[client_name]=None
|
||||||
|
client_selected[client_name]=None
|
||||||
|
|
||||||
|
""" server side list of supported ciphers """
|
||||||
list_of_ciphers = []
|
list_of_ciphers = []
|
||||||
FF_RC4_Pref = None
|
|
||||||
FF_selected = None
|
|
||||||
ADH = False
|
ADH = False
|
||||||
DHE = False
|
DHE = False
|
||||||
AECDH = False
|
AECDH = False
|
||||||
@ -133,15 +153,17 @@ for r,d,flist in os.walk(path):
|
|||||||
list_of_ciphers.append(entry['cipher'])
|
list_of_ciphers.append(entry['cipher'])
|
||||||
|
|
||||||
# check if the advertised ciphers are not effectively RC4 Only
|
# check if the advertised ciphers are not effectively RC4 Only
|
||||||
# for firefox or incompatible with firefox
|
# for clients or incompatible with them
|
||||||
if entry['cipher'] in firefox_ciphers:
|
for client_name in client_ciphers:
|
||||||
# if this is first cipher and we already are getting RC4
|
if entry['cipher'] in client_ciphers[client_name]:
|
||||||
# then it means that RC4 is preferred
|
# if this is first cipher and we already are getting RC4
|
||||||
FF_compat = True
|
# then it means that RC4 is preferred (and client is
|
||||||
if not 'RC4' in entry['cipher']:
|
# compatible with server)
|
||||||
RC4_Only_FF = False
|
client_compat[client_name]=True
|
||||||
else:
|
if not 'RC4' in entry['cipher']:
|
||||||
temp_FF_incompat[entry['cipher']] = 1
|
client_RC4_Only[client_name] = False
|
||||||
|
else:
|
||||||
|
temp_client_incompat[client_name][entry['cipher']] = 1
|
||||||
|
|
||||||
""" store the ciphers supported """
|
""" store the ciphers supported """
|
||||||
if 'ADH' in entry['cipher'] or 'AECDH' in entry['cipher']:
|
if 'ADH' in entry['cipher'] or 'AECDH' in entry['cipher']:
|
||||||
@ -279,22 +301,23 @@ for r,d,flist in os.walk(path):
|
|||||||
else:
|
else:
|
||||||
cipherordering['Unknown'] += 1
|
cipherordering['Unknown'] += 1
|
||||||
|
|
||||||
""" simulate handshake with Firefox """
|
""" simulate handshake with clients """
|
||||||
if FF_compat:
|
for client_name in client_ciphers:
|
||||||
if 'serverside' in results and results['serverside'] == "False":
|
if client_compat[client_name]:
|
||||||
for cipher in firefox_ciphers:
|
if 'serverside' in results and results['serverside'] == "False":
|
||||||
if cipher in list_of_ciphers:
|
for cipher in client_ciphers[client_name]:
|
||||||
FF_selected = cipher
|
if cipher in list_of_ciphers:
|
||||||
if 'RC4' in cipher:
|
client_selected[client_name] = cipher
|
||||||
FF_RC4_Pref = True
|
if 'RC4' in cipher:
|
||||||
break
|
client_RC4_Pref[client_name] = True
|
||||||
else:
|
break
|
||||||
for cipher in list_of_ciphers:
|
else:
|
||||||
if cipher in firefox_ciphers:
|
for cipher in list_of_ciphers:
|
||||||
FF_selected = cipher
|
if cipher in client_ciphers[client_name]:
|
||||||
if 'RC4' in cipher:
|
client_selected[client_name] = cipher
|
||||||
FF_RC4_Pref = True
|
if 'RC4' in cipher:
|
||||||
break
|
client_RC4_Pref[client_name] = True
|
||||||
|
break
|
||||||
|
|
||||||
for s in tempsigstats:
|
for s in tempsigstats:
|
||||||
sigalg[s] += 1
|
sigalg[s] += 1
|
||||||
@ -346,26 +369,29 @@ for r,d,flist in os.walk(path):
|
|||||||
cipherstats['RC4 forced in TLS1.1+'] += 1
|
cipherstats['RC4 forced in TLS1.1+'] += 1
|
||||||
cipherstats['RC4 Preferred'] += 1
|
cipherstats['RC4 Preferred'] += 1
|
||||||
|
|
||||||
if FF_compat:
|
for client_name in client_ciphers:
|
||||||
if 'ECDHE' in FF_selected:
|
if client_compat[client_name]:
|
||||||
FF_selected_cipherstats['x:ECDHE'] += 1
|
if 'ECDHE' in client_selected[client_name]:
|
||||||
elif 'DHE' in FF_selected or 'EDH' in FF_selected:
|
client_selected_cipherstats[client_name]['x:ECDHE'] += 1
|
||||||
FF_selected_cipherstats['x:DHE'] += 1
|
elif 'DHE' in client_selected[client_name]:
|
||||||
|
client_selected_cipherstats[client_name]['x:DHE'] += 1
|
||||||
|
else:
|
||||||
|
client_selected_cipherstats[client_name]['x:kRSA'] += 1
|
||||||
|
|
||||||
|
client_selected_cipherstats[client_name][client_selected[client_name]] += 1
|
||||||
|
|
||||||
|
if client_RC4_Only[client_name] and ciphertypes != 1:
|
||||||
|
cipherstats['x:' + client_name + ' RC4 Only'] += 1
|
||||||
|
for cipher in temp_client_incompat[client_name]:
|
||||||
|
client_RC4_Only_cipherstats[client_name][cipher] += 1
|
||||||
|
if client_RC4_Pref[client_name] and not 'RC4' in results['ciphersuite'][0]['cipher']:
|
||||||
|
cipherstats['x:' + client_name + ' RC4 Preferred'] += 1
|
||||||
|
for cipher in temp_client_incompat[client_name]:
|
||||||
|
client_RC4_preferred_cipherstats[client_name][cipher] += 1
|
||||||
else:
|
else:
|
||||||
FF_selected_cipherstats['x:kRSA'] += 1
|
cipherstats['x:' + client_name + ' incompatible'] += 1
|
||||||
FF_selected_cipherstats[FF_selected] += 1
|
for cipher in temp_client_incompat[client_name]:
|
||||||
if RC4_Only_FF and ciphertypes != 1:
|
client_incompatible_cipherstats[client_name][cipher] += 1
|
||||||
cipherstats['x:FF 29 RC4 Only'] += 1
|
|
||||||
for cipher in temp_FF_incompat:
|
|
||||||
FF_RC4_Only_cipherstats[cipher] += 1
|
|
||||||
if FF_RC4_Pref and not 'RC4' in results['ciphersuite'][0]['cipher']:
|
|
||||||
cipherstats['x:FF 29 RC4 Preferred'] += 1
|
|
||||||
for cipher in temp_FF_incompat:
|
|
||||||
FF_RC4_preferred_cipherstats[cipher] += 1
|
|
||||||
else:
|
|
||||||
cipherstats['x:FF 29 incompatible'] += 1
|
|
||||||
for cipher in temp_FF_incompat:
|
|
||||||
FF_incompatible_cipherstats[cipher] += 1
|
|
||||||
|
|
||||||
for cipher in tempcipherstats:
|
for cipher in tempcipherstats:
|
||||||
cipherstats[cipher] += 1
|
cipherstats[cipher] += 1
|
||||||
@ -420,12 +446,13 @@ for r,d,flist in os.walk(path):
|
|||||||
#if total % 1999 == 0:
|
#if total % 1999 == 0:
|
||||||
# break
|
# break
|
||||||
|
|
||||||
""" The 'x:FF 29 RC4 Preferred' counts only sites that effectively prefer
|
""" The 'x:' + client_name + ' RC4 Preferred' counts only sites that
|
||||||
RC4 when using FF, to make reporting more readable, sum it with sites
|
effectively prefer RC4 when using given client, to make reporting more
|
||||||
that do that for all ciphers"""
|
readable, sum it with sites that do that for all ciphers"""
|
||||||
|
|
||||||
if "x:FF 29 RC4 Preferred" in cipherstats and "RC4 Preferred" in cipherstats:
|
for client_name in client_ciphers:
|
||||||
cipherstats['x:FF 29 RC4 Preferred'] += cipherstats['RC4 Preferred']
|
if 'x:' + client_name + ' RC4 Preferred' in cipherstats and 'RC4 Preferred' in cipherstats:
|
||||||
|
cipherstats['x:' + client_name + ' RC4 Preferred'] += cipherstats['RC4 Preferred']
|
||||||
|
|
||||||
print("SSL/TLS survey of %i websites from Alexa's top 1 million" % total)
|
print("SSL/TLS survey of %i websites from Alexa's top 1 million" % total)
|
||||||
if report_untrused == False:
|
if report_untrused == False:
|
||||||
@ -445,29 +472,32 @@ for stat in sorted(cipherordering):
|
|||||||
percent = round(cipherordering[stat] / total * 100, 4)
|
percent = round(cipherordering[stat] / total * 100, 4)
|
||||||
sys.stdout.write(stat.ljust(25) + " " + str(cipherordering[stat]).ljust(10) + str(percent).ljust(4) + "\n")
|
sys.stdout.write(stat.ljust(25) + " " + str(cipherordering[stat]).ljust(10) + str(percent).ljust(4) + "\n")
|
||||||
|
|
||||||
print("\nFF 29 selected ciphers Count Percent")
|
print("\nCLIENT specific statistics\n")
|
||||||
print("-----------------------------+---------+------")
|
|
||||||
for stat in sorted(FF_selected_cipherstats):
|
|
||||||
percent = round(FF_selected_cipherstats[stat] / total * 100, 4)
|
|
||||||
sys.stdout.write(stat.ljust(30) + " " + str(FF_selected_cipherstats[stat]).ljust(10) + str(percent).ljust(4) + "\n")
|
|
||||||
|
|
||||||
print("\nFF 29 RC4 Only other ciphers Count Percent")
|
for client_name in client_ciphers:
|
||||||
print("-----------------------------+---------+------")
|
print("\n" + client_name + " selected ciphers Count Percent")
|
||||||
for stat in sorted(FF_RC4_Only_cipherstats):
|
print("-----------------------------+---------+------")
|
||||||
percent = round(FF_RC4_Only_cipherstats[stat] / total * 100, 4)
|
for stat in sorted(client_selected_cipherstats[client_name]):
|
||||||
sys.stdout.write(stat.ljust(30) + " " + str(FF_RC4_Only_cipherstats[stat]).ljust(10) + str(percent).ljust(4) + "\n")
|
percent = round(client_selected_cipherstats[client_name][stat] / total * 100, 4)
|
||||||
|
sys.stdout.write(stat.ljust(30) + " " + str(client_selected_cipherstats[client_name][stat]).ljust(10) + str(percent).ljust(4) + "\n")
|
||||||
|
|
||||||
print("\nFF 29 RC4 pref other ciphers Count Percent")
|
print("\n" + client_name + " RC4 Only other ciphers Count Percent")
|
||||||
print("-----------------------------+---------+------")
|
print("-----------------------------+---------+------")
|
||||||
for stat in sorted(FF_RC4_preferred_cipherstats):
|
for stat in sorted(client_RC4_Only_cipherstats[client_name]):
|
||||||
percent = round(FF_RC4_preferred_cipherstats[stat] / total * 100, 4)
|
percent = round(client_RC4_Only_cipherstats[client_name][stat] / total * 100, 4)
|
||||||
sys.stdout.write(stat.ljust(30) + " " + str(FF_RC4_preferred_cipherstats[stat]).ljust(10) + str(percent).ljust(4) + "\n")
|
sys.stdout.write(stat.ljust(30) + " " + str(client_RC4_Only_cipherstats[client_name][stat]).ljust(10) + str(percent).ljust(4) + "\n")
|
||||||
|
|
||||||
print("\nFF 29 incompatible ciphers Count Percent")
|
print("\n" + client_name + " RC4 pref other ciphers Count Percent")
|
||||||
print("-----------------------------+---------+------")
|
print("-----------------------------+---------+------")
|
||||||
for stat in sorted(FF_incompatible_cipherstats):
|
for stat in sorted(client_RC4_preferred_cipherstats[client_name]):
|
||||||
percent = round(FF_incompatible_cipherstats[stat] / total * 100, 4)
|
percent = round(client_RC4_preferred_cipherstats[client_name][stat] / total * 100, 4)
|
||||||
sys.stdout.write(stat.ljust(30) + " " + str(FF_incompatible_cipherstats[stat]).ljust(10) + str(percent).ljust(4) + "\n")
|
sys.stdout.write(stat.ljust(30) + " " + str(client_RC4_preferred_cipherstats[client_name][stat]).ljust(10) + str(percent).ljust(4) + "\n")
|
||||||
|
|
||||||
|
print("\n" + client_name + " incompatible ciphers Count Percent")
|
||||||
|
print("-----------------------------+---------+------")
|
||||||
|
for stat in sorted(client_incompatible_cipherstats[client_name]):
|
||||||
|
percent = round(client_incompatible_cipherstats[client_name][stat] / total * 100, 4)
|
||||||
|
sys.stdout.write(stat.ljust(30) + " " + str(client_incompatible_cipherstats[client_name][stat]).ljust(10) + str(percent).ljust(4) + "\n")
|
||||||
|
|
||||||
print("\nSupported Handshakes Count Percent")
|
print("\nSupported Handshakes Count Percent")
|
||||||
print("-------------------------+---------+-------")
|
print("-------------------------+---------+-------")
|
||||||
|
Loading…
Reference in New Issue
Block a user