3 # Copyright 2011-2019, The Tor Project, Inc
4 # original version by Arturo Filastò
5 # See LICENSE for licensing information
7 # This script parses Firefox and OpenSSL sources, and uses this information
8 # to generate a ciphers.inc file.
10 # It takes two arguments: the location of a firefox source directory, and the
11 # location of an openssl source directory.
17 if len(sys
.argv
) != 3:
18 print >>sys
.stderr
, "Syntax: get_mozilla_ciphers.py <firefox-source-dir> <openssl-source-dir>"
22 ossl_root
= sys
.argv
[2]
25 return os
.path
.join(ff_root
, s
)
27 return os
.path
.join(ossl_root
, s
)
30 # Read the cpp file to understand what Ciphers map to what name :
31 # Make "ciphers" a map from name used in the javascript to a cipher macro name
32 fileA
= open(ff('security/manager/ssl/nsNSSComponent.cpp'),'r')
34 # The input format is a file containing exactly one section of the form:
35 # static CipherPref CipherPrefs[] = {
36 # {"name", MACRO_NAME}, // comment
41 inCipherSection
= False
44 if line
.startswith('static const CipherPref sCipherPrefs[]'):
45 # Get the starting boundary of the Cipher Preferences
46 inCipherSection
= True
49 if line
.startswith('{ nullptr, 0}'):
50 # At the ending boundary of the Cipher Prefs
53 cipherLines
.append(line
)
56 # Parse the lines and put them into a dict
60 for line
in cipherLines
:
61 m
= re
.search(r
'^{\s*\"([^\"]+)\",\s*(\S+)\s*(?:,\s*(true|false))?\s*}', line
)
63 assert not key_pending
64 key
,value
,enabled
= m
.groups()
67 cipher_pref
[value
] = key
69 m
= re
.search(r
'^{\s*\"([^\"]+)\",', line
)
71 assert not key_pending
72 key_pending
= m
.group(1)
74 m
= re
.search(r
'^\s*(\S+)(?:,\s*(true|false))+\s*}', line
)
78 value
,enabled
= m
.groups()
82 cipher_pref
[value
] = key
85 # Now find the correct order for the ciphers
86 fileC
= open(ff('security/nss/lib/ssl/ssl3con.c'), 'r')
91 if "ssl3CipherSuiteCfg cipherSuites[" in line
:
95 if line
.startswith("};"):
98 m
= re
.match(r
'^\s*\{\s*([A-Z_0-9]+),', line
)
100 firefox_ciphers
.append(m
.group(1))
105 # Read the JS file to understand what ciphers are enabled. The format is
106 # pref("name", true/false);
107 # Build a map enabled_ciphers from javascript name to "true" or "false",
108 # and an (unordered!) list of the macro names for those ciphers that are
110 fileB
= open(ff('netwerk/base/security-prefs.js'), 'r')
114 m
= re
.match(r
'pref\(\"([^\"]+)\"\s*,\s*(\S*)\s*\)', line
)
117 key
, val
= m
.groups()
118 if key
.startswith("security.ssl3"):
119 enabled_ciphers
[key
] = val
123 for k
, v
in enabled_ciphers
.items():
125 used_ciphers
.append(ciphers
[k
])
127 #oSSLinclude = ('/usr/include/openssl/ssl3.h', '/usr/include/openssl/ssl.h',
128 # '/usr/include/openssl/ssl2.h', '/usr/include/openssl/ssl23.h',
129 # '/usr/include/openssl/tls1.h')
130 oSSLinclude
= ['ssl3.h', 'ssl.h'
135 # This reads the hex code for the ciphers that are used by firefox.
136 # sslProtoD is set to a map from macro name to macro value in sslproto.h;
137 # cipher_codes is set to an (unordered!) list of these hex values.
138 sslProto
= open(ff('security/nss/lib/ssl/sslproto.h'), 'r')
141 for line
in sslProto
:
142 m
= re
.match('#define\s+(\S+)\s+(\S+)', line
)
144 key
, value
= m
.groups()
145 sslProtoD
[key
] = value
149 for x
in used_ciphers
:
150 cipher_codes
.append(sslProtoD
[x
].lower())
153 # Now read through all the openssl include files, and try to find the openssl
154 # macro names for those files.
155 openssl_macro_by_hex
= {}
156 all_openssl_macros
= {}
157 for fl
in oSSLinclude
:
158 fname
= ossl("include/openssl/"+fl
)
159 if not os
.path
.exists(fname
):
161 fp
= open(fname
, 'r')
162 for line
in fp
.readlines():
163 m
= re
.match('# *define\s+(\S+)\s+(\S+)', line
)
165 value
,key
= m
.groups()
166 if key
.startswith('0x') and "_CK_" in value
:
167 key
= key
.replace('0x0300','0x').lower()
168 #print "%s %s" % (key, value)
169 openssl_macro_by_hex
[key
] = value
170 all_openssl_macros
[value
]=key
173 # Now generate the output.
175 /* This is an include file used to define the list of ciphers clients should
176 * advertise. Before including it, you should define the CIPHER and XCIPHER
179 * This file was automatically generated by get_mozilla_ciphers.py.
181 # Go in order by the order in CipherPrefs
182 for firefox_macro
in firefox_ciphers
:
185 js_cipher_name
= cipher_pref
[firefox_macro
]
187 # This one has no javascript preference.
190 # The cipher needs to be enabled in security-prefs.js
191 if enabled_ciphers
.get(js_cipher_name
, 'false') != 'true':
194 hexval
= sslProtoD
[firefox_macro
].lower()
197 openssl_macro
= openssl_macro_by_hex
[hexval
.lower()]
198 openssl_macro
= openssl_macro
.replace("_CK_", "_TXT_")
199 if openssl_macro
not in all_openssl_macros
:
201 format
= {'hex':hexval
, 'macro':openssl_macro
, 'note':""}
203 # openssl doesn't have a macro for this.
204 format
= {'hex':hexval
, 'macro':firefox_macro
,
205 'note':"/* No openssl macro found for "+hexval
+" */\n"}
208 %(note)s#ifdef %(macro)s
209 CIPHER(%(hex)s, %(macro)s)
211 XCIPHER(%(hex)s, %(macro)s)