aboutsummaryrefslogtreecommitdiff
path: root/scripts/codegen/gen_server_ciphers.py
blob: 7b61d865a2e7e9e6551b7e3357db7c3e5a658ea2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/usr/bin/python
# Copyright 2014-2015, The Tor Project, Inc
# See LICENSE for licensing information

# This script parses openssl headers to find ciphersuite names, determines
# which ones we should be willing to use as a server, and sorts them according
# to preference rules.
#
# Run it on all the files in your openssl include directory.

import re
import sys

EPHEMERAL_INDICATORS = [ "_EDH_", "_DHE_", "_ECDHE_" ]
BAD_STUFF = [ "_DES_40_", "MD5", "_RC4_", "_DES_64_",
              "_SEED_", "_CAMELLIA_", "_NULL",
              "_CCM_8", "_DES_", ]

# these never get #ifdeffed.
MANDATORY = [
    "TLS1_TXT_DHE_RSA_WITH_AES_256_SHA",
    "TLS1_TXT_DHE_RSA_WITH_AES_128_SHA",
    "SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA",
]

def find_ciphers(filename):
    with open(filename) as f:
        for line in f:
            m = re.search(r'(?:SSL3|TLS1)_TXT_\w+', line)
            if m:
                yield m.group(0)

def usable_cipher(ciph):
    ephemeral = False
    for e in EPHEMERAL_INDICATORS:
        if e in ciph:
            ephemeral = True
    if not ephemeral:
        return False

    if "_RSA_" not in ciph:
        return False

    for b in BAD_STUFF:
        if b in ciph:
            return False
    return True

# All fields we sort on, in order of priority.
FIELDS = [ 'cipher', 'fwsec', 'mode',  'digest', 'bitlength' ]
# Map from sorted fields to recognized value in descending order of goodness
FIELD_VALS = { 'cipher' : [ 'AES', 'CHACHA20' ],
               'fwsec' : [ 'ECDHE', 'DHE' ],
               'mode' : [ 'POLY1305', 'GCM', 'CCM', 'CBC', ],
               'digest' : [ 'n/a', 'SHA384', 'SHA256', 'SHA', ],
               'bitlength' : [ '256', '128', '192' ],
}

class Ciphersuite(object):
    def __init__(self, name, fwsec, cipher, bitlength, mode, digest):
        if fwsec == 'EDH':
            fwsec = 'DHE'

        if mode in [ '_CBC3', '_CBC', '' ]:
            mode = 'CBC'
        elif mode == '_GCM':
            mode = 'GCM'

        self.name = name
        self.fwsec = fwsec
        self.cipher = cipher
        self.bitlength = bitlength
        self.mode = mode
        self.digest = digest

        for f in FIELDS:
            assert(getattr(self, f) in FIELD_VALS[f])

    def sort_key(self):
        return tuple(FIELD_VALS[f].index(getattr(self,f)) for f in FIELDS)


def parse_cipher(ciph):
    m = re.match('(?:TLS1|SSL3)_TXT_(EDH|DHE|ECDHE)_RSA(?:_WITH)?_(AES|DES)_(256|128|192)(|_CBC|_CBC3|_GCM)_(SHA|SHA256|SHA384)$', ciph)

    if m:
        fwsec, cipher, bits, mode, digest = m.groups()
        return Ciphersuite(ciph, fwsec, cipher, bits, mode, digest)

    m = re.match('(?:TLS1|SSL3)_TXT_(EDH|DHE|ECDHE)_RSA(?:_WITH)?_(AES|DES)_(256|128|192)_CCM', ciph)
    if m:
        fwsec, cipher, bits = m.groups()
        return Ciphersuite(ciph, fwsec, cipher, bits, "CCM", "n/a")

    m = re.match('(?:TLS1|SSL3)_TXT_(EDH|DHE|ECDHE)_RSA(?:_WITH)?_CHACHA20_POLY1305', ciph)
    if m:
        fwsec, = m.groups()
        return Ciphersuite(ciph, fwsec, "CHACHA20", "256", "POLY1305", "n/a")

    print "/* Couldn't parse %s ! */"%ciph
    return None


ALL_CIPHERS = []

for fname in sys.argv[1:]:
    for c in find_ciphers(fname):
        if usable_cipher(c):
            parsed = parse_cipher(c)
            if parsed != None:
                ALL_CIPHERS.append(parsed)

ALL_CIPHERS.sort(key=Ciphersuite.sort_key)

for c in ALL_CIPHERS:
    if c is ALL_CIPHERS[-1]:
        colon = ';'
    else:
        colon = ' ":"'

    if c.name in MANDATORY:
        print "       /* Required */"
        print '       %s%s'%(c.name,colon)
    else:
        print "#ifdef %s"%c.name
        print '       %s%s'%(c.name,colon)
        print "#endif"