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.
13 # Future imports for Python 2.7, mandatory in 3.0
14 from __future__
import division
15 from __future__
import print_function
16 from __future__
import unicode_literals
22 if len(sys
.argv
) != 3:
23 print("Syntax: get_mozilla_ciphers.py <firefox-source-dir> <openssl-source-dir>", file=sys
.stderr
)
27 ossl_root
= sys
.argv
[2]
30 return os
.path
.join(ff_root
, s
)
32 return os
.path
.join(ossl_root
, s
)
35 # Read the cpp file to understand what Ciphers map to what name :
36 # Make "ciphers" a map from name used in the javascript to a cipher macro name
37 fileA
= open(ff('security/manager/ssl/nsNSSComponent.cpp'),'r')
39 # The input format is a file containing exactly one section of the form:
40 # static CipherPref CipherPrefs[] = {
41 # {"name", MACRO_NAME}, // comment
46 inCipherSection
= False
49 if line
.startswith('static const CipherPref sCipherPrefs[]'):
50 # Get the starting boundary of the Cipher Preferences
51 inCipherSection
= True
54 if line
.startswith('{ nullptr, 0}'):
55 # At the ending boundary of the Cipher Prefs
58 cipherLines
.append(line
)
61 # Parse the lines and put them into a dict
65 for line
in cipherLines
:
66 m
= re
.search(r
'^{\s*\"([^\"]+)\",\s*(\S+)\s*(?:,\s*(true|false))?\s*}', line
)
68 assert not key_pending
69 key
,value
,enabled
= m
.groups()
72 cipher_pref
[value
] = key
74 m
= re
.search(r
'^{\s*\"([^\"]+)\",', line
)
76 assert not key_pending
77 key_pending
= m
.group(1)
79 m
= re
.search(r
'^\s*(\S+)(?:,\s*(true|false))+\s*}', line
)
83 value
,enabled
= m
.groups()
87 cipher_pref
[value
] = key
90 # Now find the correct order for the ciphers
91 fileC
= open(ff('security/nss/lib/ssl/ssl3con.c'), 'r')
96 if "ssl3CipherSuiteCfg cipherSuites[" in line
:
100 if line
.startswith("};"):
103 m
= re
.match(r
'^\s*\{\s*([A-Z_0-9]+),', line
)
105 firefox_ciphers
.append(m
.group(1))
110 # Read the JS file to understand what ciphers are enabled. The format is
111 # pref("name", true/false);
112 # Build a map enabled_ciphers from javascript name to "true" or "false",
113 # and an (unordered!) list of the macro names for those ciphers that are
115 fileB
= open(ff('netwerk/base/security-prefs.js'), 'r')
119 m
= re
.match(r
'pref\(\"([^\"]+)\"\s*,\s*(\S*)\s*\)', line
)
122 key
, val
= m
.groups()
123 if key
.startswith("security.ssl3"):
124 enabled_ciphers
[key
] = val
128 for k
, v
in enabled_ciphers
.items():
130 used_ciphers
.append(ciphers
[k
])
132 #oSSLinclude = ('/usr/include/openssl/ssl3.h', '/usr/include/openssl/ssl.h',
133 # '/usr/include/openssl/ssl2.h', '/usr/include/openssl/ssl23.h',
134 # '/usr/include/openssl/tls1.h')
135 oSSLinclude
= ['ssl3.h', 'ssl.h'
140 # This reads the hex code for the ciphers that are used by firefox.
141 # sslProtoD is set to a map from macro name to macro value in sslproto.h;
142 # cipher_codes is set to an (unordered!) list of these hex values.
143 sslProto
= open(ff('security/nss/lib/ssl/sslproto.h'), 'r')
146 for line
in sslProto
:
147 m
= re
.match(r
'#define\s+(\S+)\s+(\S+)', line
)
149 key
, value
= m
.groups()
150 sslProtoD
[key
] = value
154 for x
in used_ciphers
:
155 cipher_codes
.append(sslProtoD
[x
].lower())
158 # Now read through all the openssl include files, and try to find the openssl
159 # macro names for those files.
160 openssl_macro_by_hex
= {}
161 all_openssl_macros
= {}
162 for fl
in oSSLinclude
:
163 fname
= ossl("include/openssl/"+fl
)
164 if not os
.path
.exists(fname
):
166 fp
= open(fname
, 'r')
167 for line
in fp
.readlines():
168 m
= re
.match(r
'# *define\s+(\S+)\s+(\S+)', line
)
170 value
,key
= m
.groups()
171 if key
.startswith('0x') and "_CK_" in value
:
172 key
= key
.replace('0x0300','0x').lower()
173 #print "%s %s" % (key, value)
174 openssl_macro_by_hex
[key
] = value
175 all_openssl_macros
[value
]=key
178 # Now generate the output.
180 /* This is an include file used to define the list of ciphers clients should
181 * advertise. Before including it, you should define the CIPHER and XCIPHER
184 * This file was automatically generated by get_mozilla_ciphers.py.
186 # Go in order by the order in CipherPrefs
187 for firefox_macro
in firefox_ciphers
:
190 js_cipher_name
= cipher_pref
[firefox_macro
]
192 # This one has no javascript preference.
195 # The cipher needs to be enabled in security-prefs.js
196 if enabled_ciphers
.get(js_cipher_name
, 'false') != 'true':
199 hexval
= sslProtoD
[firefox_macro
].lower()
202 openssl_macro
= openssl_macro_by_hex
[hexval
.lower()]
203 openssl_macro
= openssl_macro
.replace("_CK_", "_TXT_")
204 if openssl_macro
not in all_openssl_macros
:
206 format
= {'hex':hexval
, 'macro':openssl_macro
, 'note':""}
208 # openssl doesn't have a macro for this.
209 format
= {'hex':hexval
, 'macro':firefox_macro
,
210 'note':"/* No openssl macro found for "+hexval
+" */\n"}
213 %(note)s#ifdef %(macro)s
214 CIPHER(%(hex)s, %(macro)s)
216 XCIPHER(%(hex)s, %(macro)s)