gen_server_ciphers.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. #!/usr/bin/env 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. # Future imports for Python 2.7, mandatory in 3.0
  10. from __future__ import division
  11. from __future__ import print_function
  12. from __future__ import unicode_literals
  13. import re
  14. import sys
  15. EPHEMERAL_INDICATORS = [ "_EDH_", "_DHE_", "_ECDHE_" ]
  16. BAD_STUFF = [ "_DES_40_", "MD5", "_RC4_", "_DES_64_",
  17. "_SEED_", "_CAMELLIA_", "_NULL",
  18. "_CCM_8", "_DES_", ]
  19. # these never get #ifdeffed.
  20. MANDATORY = [
  21. "TLS1_TXT_DHE_RSA_WITH_AES_256_SHA",
  22. "TLS1_TXT_DHE_RSA_WITH_AES_128_SHA",
  23. ]
  24. def find_ciphers(filename):
  25. with open(filename) as f:
  26. for line in f:
  27. m = re.search(r'(?:SSL3|TLS1)_TXT_\w+', line)
  28. if m:
  29. yield m.group(0)
  30. def usable_cipher(ciph):
  31. ephemeral = False
  32. for e in EPHEMERAL_INDICATORS:
  33. if e in ciph:
  34. ephemeral = True
  35. if not ephemeral:
  36. return False
  37. if "_RSA_" not in ciph:
  38. return False
  39. for b in BAD_STUFF:
  40. if b in ciph:
  41. return False
  42. return True
  43. # All fields we sort on, in order of priority.
  44. FIELDS = [ 'cipher', 'fwsec', 'mode', 'digest', 'bitlength' ]
  45. # Map from sorted fields to recognized value in descending order of goodness
  46. FIELD_VALS = { 'cipher' : [ 'AES', 'CHACHA20' ],
  47. 'fwsec' : [ 'ECDHE', 'DHE' ],
  48. 'mode' : [ 'POLY1305', 'GCM', 'CCM', 'CBC', ],
  49. 'digest' : [ 'n/a', 'SHA384', 'SHA256', 'SHA', ],
  50. 'bitlength' : [ '256', '128', '192' ],
  51. }
  52. class Ciphersuite(object):
  53. def __init__(self, name, fwsec, cipher, bitlength, mode, digest):
  54. if fwsec == 'EDH':
  55. fwsec = 'DHE'
  56. if mode in [ '_CBC3', '_CBC', '' ]:
  57. mode = 'CBC'
  58. elif mode == '_GCM':
  59. mode = 'GCM'
  60. self.name = name
  61. self.fwsec = fwsec
  62. self.cipher = cipher
  63. self.bitlength = bitlength
  64. self.mode = mode
  65. self.digest = digest
  66. for f in FIELDS:
  67. assert(getattr(self, f) in FIELD_VALS[f])
  68. def sort_key(self):
  69. return tuple(FIELD_VALS[f].index(getattr(self,f)) for f in FIELDS)
  70. def parse_cipher(ciph):
  71. m = re.match('(?:TLS1|SSL3)_TXT_(EDH|DHE|ECDHE)_RSA(?:_WITH)?_(AES|DES)_(256|128|192)(|_CBC|_CBC3|_GCM)_(SHA|SHA256|SHA384)$', ciph)
  72. if m:
  73. fwsec, cipher, bits, mode, digest = m.groups()
  74. return Ciphersuite(ciph, fwsec, cipher, bits, mode, digest)
  75. m = re.match('(?:TLS1|SSL3)_TXT_(EDH|DHE|ECDHE)_RSA(?:_WITH)?_(AES|DES)_(256|128|192)_CCM', ciph)
  76. if m:
  77. fwsec, cipher, bits = m.groups()
  78. return Ciphersuite(ciph, fwsec, cipher, bits, "CCM", "n/a")
  79. m = re.match('(?:TLS1|SSL3)_TXT_(EDH|DHE|ECDHE)_RSA(?:_WITH)?_CHACHA20_POLY1305', ciph)
  80. if m:
  81. fwsec, = m.groups()
  82. return Ciphersuite(ciph, fwsec, "CHACHA20", "256", "POLY1305", "n/a")
  83. print("/* Couldn't parse %s ! */"%ciph)
  84. return None
  85. ALL_CIPHERS = []
  86. for fname in sys.argv[1:]:
  87. for c in find_ciphers(fname):
  88. if usable_cipher(c):
  89. parsed = parse_cipher(c)
  90. if parsed != None:
  91. ALL_CIPHERS.append(parsed)
  92. ALL_CIPHERS.sort(key=Ciphersuite.sort_key)
  93. indent = " "*7
  94. for c in ALL_CIPHERS:
  95. if c is ALL_CIPHERS[-1]:
  96. colon = ''
  97. else:
  98. colon = ' ":"'
  99. if c.name in MANDATORY:
  100. print("%s/* Required */"%indent)
  101. print('%s%s%s'%(indent,c.name,colon))
  102. else:
  103. print("#ifdef %s"%c.name)
  104. print('%s%s%s'%(indent,c.name,colon))
  105. print("#endif")
  106. print('%s;'%indent)