gen_server_ciphers.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  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. # these never get #ifdeffed.
  15. MANDATORY = [
  16. "TLS1_TXT_DHE_RSA_WITH_AES_256_SHA",
  17. "TLS1_TXT_DHE_RSA_WITH_AES_128_SHA",
  18. "SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA",
  19. ]
  20. def find_ciphers(filename):
  21. with open(filename) as f:
  22. for line in f:
  23. m = re.search(r'(?:SSL3|TLS1)_TXT_\w+', line)
  24. if m:
  25. yield m.group(0)
  26. def usable_cipher(ciph):
  27. ephemeral = False
  28. for e in EPHEMERAL_INDICATORS:
  29. if e in ciph:
  30. ephemeral = True
  31. if not ephemeral:
  32. return False
  33. if "_RSA_" not in ciph:
  34. return False
  35. for b in BAD_STUFF:
  36. if b in ciph:
  37. return False
  38. return True
  39. # All fields we sort on, in order of priority.
  40. FIELDS = [ 'cipher', 'fwsec', 'mode', 'digest', 'bitlength' ]
  41. # Map from sorted fields to recognized value in descending order of goodness
  42. FIELD_VALS = { 'cipher' : [ 'AES', 'DES'],
  43. 'fwsec' : [ 'ECDHE', 'DHE' ],
  44. 'mode' : [ 'GCM', 'CBC' ],
  45. 'digest' : [ 'SHA384', 'SHA256', 'SHA' ],
  46. 'bitlength' : [ '256', '128', '192' ],
  47. }
  48. class Ciphersuite(object):
  49. def __init__(self, name, fwsec, cipher, bitlength, mode, digest):
  50. self.name = name
  51. self.fwsec = fwsec
  52. self.cipher = cipher
  53. self.bitlength = bitlength
  54. self.mode = mode
  55. self.digest = digest
  56. for f in FIELDS:
  57. assert(getattr(self, f) in FIELD_VALS[f])
  58. def sort_key(self):
  59. return tuple(FIELD_VALS[f].index(getattr(self,f)) for f in FIELDS)
  60. def parse_cipher(ciph):
  61. m = re.match('(?:TLS1|SSL3)_TXT_(EDH|DHE|ECDHE)_RSA(?:_WITH)?_(AES|DES)_(256|128|192)(|_CBC|_CBC3|_GCM)_(SHA|SHA256|SHA384)$', ciph)
  62. if not m:
  63. print "/* Couldn't parse %s ! */"%ciph
  64. return None
  65. fwsec, cipher, bits, mode, digest = m.groups()
  66. if fwsec == 'EDH':
  67. fwsec = 'DHE'
  68. if mode in [ '_CBC3', '_CBC', '' ]:
  69. mode = 'CBC'
  70. elif mode == '_GCM':
  71. mode = 'GCM'
  72. return Ciphersuite(ciph, fwsec, cipher, bits, mode, digest)
  73. ALL_CIPHERS = []
  74. for fname in sys.argv[1:]:
  75. ALL_CIPHERS += (parse_cipher(c)
  76. for c in find_ciphers(fname)
  77. if usable_cipher(c) )
  78. ALL_CIPHERS.sort(key=Ciphersuite.sort_key)
  79. for c in ALL_CIPHERS:
  80. if c is ALL_CIPHERS[-1]:
  81. colon = ';'
  82. else:
  83. colon = ' ":"'
  84. if c.name in MANDATORY:
  85. print " /* Required */"
  86. print ' %s%s'%(c.name,colon)
  87. else:
  88. print "#ifdef %s"%c.name
  89. print ' %s%s'%(c.name,colon)
  90. print "#endif"