gen_server_ciphers.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. #!/usr/bin/python
  2. # Copyright 2014-2015, The Tor Project, Inc
  3. # See LICENSE for licensing information
  4. # This script parses openssl headers to find ciphersuite names, determines
  5. # which ones we should be willing to use as a server, and sorts them according
  6. # to preference rules.
  7. #
  8. # Run it on all the files in your openssl include directory.
  9. import re
  10. import sys
  11. EPHEMERAL_INDICATORS = [ "_EDH_", "_DHE_", "_ECDHE_" ]
  12. BAD_STUFF = [ "_DES_40_", "MD5", "_RC4_", "_DES_64_",
  13. "_SEED_", "_CAMELLIA_", "_NULL",
  14. "_CCM_8", "_DES_", ]
  15. # these never get #ifdeffed.
  16. MANDATORY = [
  17. "TLS1_TXT_DHE_RSA_WITH_AES_256_SHA",
  18. "TLS1_TXT_DHE_RSA_WITH_AES_128_SHA",
  19. "SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA",
  20. ]
  21. def find_ciphers(filename):
  22. with open(filename) as f:
  23. for line in f:
  24. m = re.search(r'(?:SSL3|TLS1)_TXT_\w+', line)
  25. if m:
  26. yield m.group(0)
  27. def usable_cipher(ciph):
  28. ephemeral = False
  29. for e in EPHEMERAL_INDICATORS:
  30. if e in ciph:
  31. ephemeral = True
  32. if not ephemeral:
  33. return False
  34. if "_RSA_" not in ciph:
  35. return False
  36. for b in BAD_STUFF:
  37. if b in ciph:
  38. return False
  39. return True
  40. # All fields we sort on, in order of priority.
  41. FIELDS = [ 'cipher', 'fwsec', 'mode', 'digest', 'bitlength' ]
  42. # Map from sorted fields to recognized value in descending order of goodness
  43. FIELD_VALS = { 'cipher' : [ 'AES', 'CHACHA20' ],
  44. 'fwsec' : [ 'ECDHE', 'DHE' ],
  45. 'mode' : [ 'POLY1305', 'GCM', 'CCM', 'CBC', ],
  46. 'digest' : [ 'n/a', 'SHA384', 'SHA256', 'SHA', ],
  47. 'bitlength' : [ '256', '128', '192' ],
  48. }
  49. class Ciphersuite(object):
  50. def __init__(self, name, fwsec, cipher, bitlength, mode, digest):
  51. if fwsec == 'EDH':
  52. fwsec = 'DHE'
  53. if mode in [ '_CBC3', '_CBC', '' ]:
  54. mode = 'CBC'
  55. elif mode == '_GCM':
  56. mode = 'GCM'
  57. self.name = name
  58. self.fwsec = fwsec
  59. self.cipher = cipher
  60. self.bitlength = bitlength
  61. self.mode = mode
  62. self.digest = digest
  63. for f in FIELDS:
  64. assert(getattr(self, f) in FIELD_VALS[f])
  65. def sort_key(self):
  66. return tuple(FIELD_VALS[f].index(getattr(self,f)) for f in FIELDS)
  67. def parse_cipher(ciph):
  68. m = re.match('(?:TLS1|SSL3)_TXT_(EDH|DHE|ECDHE)_RSA(?:_WITH)?_(AES|DES)_(256|128|192)(|_CBC|_CBC3|_GCM)_(SHA|SHA256|SHA384)$', ciph)
  69. if m:
  70. fwsec, cipher, bits, mode, digest = m.groups()
  71. return Ciphersuite(ciph, fwsec, cipher, bits, mode, digest)
  72. m = re.match('(?:TLS1|SSL3)_TXT_(EDH|DHE|ECDHE)_RSA(?:_WITH)?_(AES|DES)_(256|128|192)_CCM', ciph)
  73. if m:
  74. fwsec, cipher, bits = m.groups()
  75. return Ciphersuite(ciph, fwsec, cipher, bits, "CCM", "n/a")
  76. m = re.match('(?:TLS1|SSL3)_TXT_(EDH|DHE|ECDHE)_RSA(?:_WITH)?_CHACHA20_POLY1305', ciph)
  77. if m:
  78. fwsec, = m.groups()
  79. return Ciphersuite(ciph, fwsec, "CHACHA20", "256", "POLY1305", "n/a")
  80. print "/* Couldn't parse %s ! */"%ciph
  81. return None
  82. ALL_CIPHERS = []
  83. for fname in sys.argv[1:]:
  84. for c in find_ciphers(fname):
  85. if usable_cipher(c):
  86. parsed = parse_cipher(c)
  87. if parsed != None:
  88. ALL_CIPHERS.append(parsed)
  89. ALL_CIPHERS.sort(key=Ciphersuite.sort_key)
  90. for c in ALL_CIPHERS:
  91. if c is ALL_CIPHERS[-1]:
  92. colon = ';'
  93. else:
  94. colon = ' ":"'
  95. if c.name in MANDATORY:
  96. print " /* Required */"
  97. print ' %s%s'%(c.name,colon)
  98. else:
  99. print "#ifdef %s"%c.name
  100. print ' %s%s'%(c.name,colon)
  101. print "#endif"