tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

gen_server_ciphers.py (3873B)


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