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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
|
#!/usr/bin/python
# coding=utf-8
# Copyright 2011, The Tor Project, Inc
# original version by Arturo Filastò
# See LICENSE for licensing information
# This script parses Firefox and OpenSSL sources, and uses this information
# to generate a ciphers.inc file.
#
# It takes two arguments: the location of a firefox source directory, and the
# location of an openssl source directory.
import os
import re
import sys
if len(sys.argv) != 3:
print >>sys.stderr, "Syntax: get_mozilla_ciphers.py <firefox-source-dir> <openssl-source-dir>"
sys.exit(1)
ff_root = sys.argv[1]
ossl_root = sys.argv[2]
def ff(s):
return os.path.join(ff_root, s)
def ossl(s):
return os.path.join(ossl_root, s)
#####
# Read the cpp file to understand what Ciphers map to what name :
# Make "ciphers" a map from name used in the javascript to a cipher macro name
fileA = open(ff('security/manager/ssl/src/nsNSSComponent.cpp'),'r')
# The input format is a file containing exactly one section of the form:
# static CipherPref CipherPrefs[] = {
# {"name", MACRO_NAME}, // comment
# ...
# {NULL, 0}
# }
inCipherSection = False
cipherLines = []
for line in fileA:
if line.startswith('static CipherPref CipherPrefs'):
# Get the starting boundary of the Cipher Preferences
inCipherSection = True
elif inCipherSection:
line = line.strip()
if line.startswith('{NULL, 0}'):
# At the ending boundary of the Cipher Prefs
break
else:
cipherLines.append(line)
fileA.close()
# Parse the lines and put them into a dict
ciphers = {}
cipher_pref = {}
for line in cipherLines:
m = re.search(r'^{\s*\"([^\"]+)\",\s*(\S*)\s*}', line)
if m:
key,value = m.groups()
ciphers[key] = value
cipher_pref[value] = key
####
# Now find the correct order for the ciphers
fileC = open(ff('security/nss/lib/ssl/ssl3con.c'), 'r')
firefox_ciphers = []
inEnum=False
for line in fileC:
if not inEnum:
if "ssl3CipherSuiteCfg cipherSuites[" in line:
inEnum = True
continue
if line.startswith("};"):
break
m = re.match(r'^\s*\{\s*([A-Z_0-9]+),', line)
if m:
firefox_ciphers.append(m.group(1))
fileC.close()
#####
# Read the JS file to understand what ciphers are enabled. The format is
# pref("name", true/false);
# Build a map enabled_ciphers from javascript name to "true" or "false",
# and an (unordered!) list of the macro names for those ciphers that are
# enabled.
fileB = open(ff('netwerk/base/public/security-prefs.js'), 'r')
enabled_ciphers = {}
for line in fileB:
m = re.match(r'pref\(\"([^\"]+)\"\s*,\s*(\S*)\s*\)', line)
if not m:
continue
key, val = m.groups()
if key.startswith("security.ssl3"):
enabled_ciphers[key] = val
fileB.close()
used_ciphers = []
for k, v in enabled_ciphers.items():
if v == "true":
used_ciphers.append(ciphers[k])
#oSSLinclude = ('/usr/include/openssl/ssl3.h', '/usr/include/openssl/ssl.h',
# '/usr/include/openssl/ssl2.h', '/usr/include/openssl/ssl23.h',
# '/usr/include/openssl/tls1.h')
oSSLinclude = ('ssl/ssl3.h', 'ssl/ssl.h',
'ssl/ssl2.h', 'ssl/ssl23.h',
'ssl/tls1.h')
#####
# This reads the hex code for the ciphers that are used by firefox.
# sslProtoD is set to a map from macro name to macro value in sslproto.h;
# cipher_codes is set to an (unordered!) list of these hex values.
sslProto = open(ff('security/nss/lib/ssl/sslproto.h'), 'r')
sslProtoD = {}
for line in sslProto:
m = re.match('#define\s+(\S+)\s+(\S+)', line)
if m:
key, value = m.groups()
sslProtoD[key] = value
sslProto.close()
cipher_codes = []
for x in used_ciphers:
cipher_codes.append(sslProtoD[x].lower())
####
# Now read through all the openssl include files, and try to find the openssl
# macro names for those files.
openssl_macro_by_hex = {}
all_openssl_macros = {}
for fl in oSSLinclude:
fp = open(ossl(fl), 'r')
for line in fp.readlines():
m = re.match('#define\s+(\S+)\s+(\S+)', line)
if m:
value,key = m.groups()
if key.startswith('0x') and "_CK_" in value:
key = key.replace('0x0300','0x').lower()
#print "%s %s" % (key, value)
openssl_macro_by_hex[key] = value
all_openssl_macros[value]=key
fp.close()
# Now generate the output.
print """\
/* This is an include file used to define the list of ciphers clients should
* advertise. Before including it, you should define the CIPHER and XCIPHER
* macros.
*
* This file was automatically generated by get_mozilla_ciphers.py.
*/"""
# Go in order by the order in CipherPrefs
for firefox_macro in firefox_ciphers:
try:
js_cipher_name = cipher_pref[firefox_macro]
except KeyError:
# This one has no javascript preference.
continue
# The cipher needs to be enabled in security-prefs.js
if enabled_ciphers.get(js_cipher_name, 'false') != 'true':
continue
hexval = sslProtoD[firefox_macro].lower()
try:
openssl_macro = openssl_macro_by_hex[hexval.lower()]
openssl_macro = openssl_macro.replace("_CK_", "_TXT_")
if openssl_macro not in all_openssl_macros:
raise KeyError()
format = {'hex':hexval, 'macro':openssl_macro, 'note':""}
except KeyError:
# openssl doesn't have a macro for this.
format = {'hex':hexval, 'macro':firefox_macro,
'note':"/* No openssl macro found for "+hexval+" */\n"}
res = """\
%(note)s#ifdef %(macro)s
CIPHER(%(hex)s, %(macro)s)
#else
XCIPHER(%(hex)s, %(macro)s)
#endif""" % format
print res
|