gen_server_ciphers.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. #!/usr/bin/python
  2. # Copyright 2014-2019, 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. ]
  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', 'CHACHA20' ],
  43. 'fwsec' : [ 'ECDHE', 'DHE' ],
  44. 'mode' : [ 'POLY1305', 'GCM', 'CCM', 'CBC', ],
  45. 'digest' : [ 'n/a', 'SHA384', 'SHA256', 'SHA', ],
  46. 'bitlength' : [ '256', '128', '192' ],
  47. }
  48. class Ciphersuite(object):
  49. def __init__(self, name, fwsec, cipher, bitlength, mode, digest):
  50. if fwsec == 'EDH':
  51. fwsec = 'DHE'
  52. if mode in [ '_CBC3', '_CBC', '' ]:
  53. mode = 'CBC'
  54. elif mode == '_GCM':
  55. mode = 'GCM'
  56. self.name = name
  57. self.fwsec = fwsec
  58. self.cipher = cipher
  59. self.bitlength = bitlength
  60. self.mode = mode
  61. self.digest = digest
  62. for f in FIELDS:
  63. assert(getattr(self, f) in FIELD_VALS[f])
  64. def sort_key(self):
  65. return tuple(FIELD_VALS[f].index(getattr(self,f)) for f in FIELDS)
  66. def parse_cipher(ciph):
  67. m = re.match('(?:TLS1|SSL3)_TXT_(EDH|DHE|ECDHE)_RSA(?:_WITH)?_(AES|DES)_(256|128|192)(|_CBC|_CBC3|_GCM)_(SHA|SHA256|SHA384)$', ciph)
  68. if m:
  69. fwsec, cipher, bits, mode, digest = m.groups()
  70. return Ciphersuite(ciph, fwsec, cipher, bits, mode, digest)
  71. m = re.match('(?:TLS1|SSL3)_TXT_(EDH|DHE|ECDHE)_RSA(?:_WITH)?_(AES|DES)_(256|128|192)_CCM', ciph)
  72. if m:
  73. fwsec, cipher, bits = m.groups()
  74. return Ciphersuite(ciph, fwsec, cipher, bits, "CCM", "n/a")
  75. m = re.match('(?:TLS1|SSL3)_TXT_(EDH|DHE|ECDHE)_RSA(?:_WITH)?_CHACHA20_POLY1305', ciph)
  76. if m:
  77. fwsec, = m.groups()
  78. return Ciphersuite(ciph, fwsec, "CHACHA20", "256", "POLY1305", "n/a")
  79. print "/* Couldn't parse %s ! */"%ciph
  80. return None
  81. ALL_CIPHERS = []
  82. for fname in sys.argv[1:]:
  83. for c in find_ciphers(fname):
  84. if usable_cipher(c):
  85. parsed = parse_cipher(c)
  86. if parsed != None:
  87. ALL_CIPHERS.append(parsed)
  88. ALL_CIPHERS.sort(key=Ciphersuite.sort_key)
  89. indent = " "*7
  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 "%s/* Required */"%indent
  97. print '%s%s%s'%(indent,c.name,colon)
  98. else:
  99. print "#ifdef %s"%c.name
  100. print '%s%s%s'%(indent,c.name,colon)
  101. print "#endif"
  102. print '%s;'%indent