2 # This Source Code Form is subject to the terms of the Mozilla Public
3 # License, v. 2.0. If a copy of the MPL was not distributed with this
4 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 # This script exists to generate the Certificate Authority and server
7 # certificates used for SSL testing in Mochitest. The already generated
8 # certs are located at $topsrcdir/build/pgo/certs/ .
16 from distutils
.spawn
import find_executable
19 from mozbuild
.base
import BinaryNotFoundException
, MozbuildObject
20 from mozfile
import NamedTemporaryFile
, TemporaryDirectory
21 from mozprofile
.permissions
import ServerLocations
24 re
.compile("^cert[0-9]+\.db$"),
25 re
.compile("^key[0-9]+\.db$"),
26 re
.compile("^secmod\.db$"),
30 def unlinkDbFiles(path
):
31 for root
, dirs
, files
in os
.walk(path
):
33 for dbFile
in dbFiles
:
34 if dbFile
.match(name
) and os
.path
.exists(os
.path
.join(root
, name
)):
35 os
.unlink(os
.path
.join(root
, name
))
38 def dbFilesExist(path
):
39 for root
, dirs
, files
in os
.walk(path
):
41 for dbFile
in dbFiles
:
42 if dbFile
.match(name
) and os
.path
.exists(os
.path
.join(root
, name
)):
47 def runUtil(util
, args
, inputdata
=None, outputstream
=None):
48 env
= os
.environ
.copy()
49 if mozinfo
.os
== "linux":
50 pathvar
= "LD_LIBRARY_PATH"
51 app_path
= os
.path
.dirname(util
)
53 env
[pathvar
] = "%s%s%s" % (app_path
, os
.pathsep
, env
[pathvar
])
55 env
[pathvar
] = app_path
56 proc
= subprocess
.Popen(
59 stdin
=subprocess
.PIPE
if inputdata
else None,
61 universal_newlines
=True,
63 proc
.communicate(inputdata
)
64 return proc
.returncode
67 def createRandomFile(randomFile
):
68 for count
in xrange(0, 2048):
69 randomFile
.write(chr(random
.randint(0, 255)))
72 def writeCertspecForServerLocations(fd
):
73 locations
= ServerLocations(
74 os
.path
.join(build
.topsrcdir
, "build", "pgo", "server-locations.txt")
78 i
for i
in iter(locations
) if i
.scheme
== "https" and "nocert" not in i
.options
80 customCertOption
= False
81 customCertRE
= re
.compile("^cert=(?:\w+)")
82 for _
in [i
for i
in loc
.options
if customCertRE
.match(i
)]:
83 customCertOption
= True
86 if "ipV4Address" in loc
.options
:
87 loc
.host
= "ip4:" + loc
.host
89 if not customCertOption
:
93 "issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization\n" # NOQA: E501
95 fd
.write("subject:{}\n".format(SAN
[0]))
96 fd
.write("extension:subjectAlternativeName:{}\n".format(",".join(SAN
)))
99 def constructCertDatabase(build
, srcDir
):
101 certutil
= build
.get_binary_path(what
="certutil")
102 pk12util
= build
.get_binary_path(what
="pk12util")
103 except BinaryNotFoundException
as e
:
104 print("{}\n\n{}\n".format(e
, e
.help()))
106 openssl
= find_executable("openssl")
107 pycert
= os
.path
.join(build
.topsrcdir
, "security", "manager", "tools", "pycert.py")
108 pykey
= os
.path
.join(build
.topsrcdir
, "security", "manager", "tools", "pykey.py")
110 with
NamedTemporaryFile(mode
="wt+") as pwfile
, TemporaryDirectory() as pemfolder
:
114 if dbFilesExist(srcDir
):
115 # Make sure all DB files from src are really deleted
116 unlinkDbFiles(srcDir
)
118 # Copy all .certspec and .keyspec files to a temporary directory
119 for root
, dirs
, files
in os
.walk(srcDir
):
121 i
for i
in files
if i
.endswith(".certspec") or i
.endswith(".keyspec")
123 shutil
.copyfile(os
.path
.join(root
, spec
), os
.path
.join(pemfolder
, spec
))
125 # Write a certspec for the "server-locations.txt" file to that temporary directory
126 pgoserver_certspec
= os
.path
.join(pemfolder
, "pgoserver.certspec")
127 if os
.path
.exists(pgoserver_certspec
):
129 "{} already exists, which isn't allowed".format(pgoserver_certspec
)
131 with
open(pgoserver_certspec
, "w") as fd
:
132 writeCertspecForServerLocations(fd
)
134 # Generate certs for all certspecs
135 for root
, dirs
, files
in os
.walk(pemfolder
):
136 for certspec
in [i
for i
in files
if i
.endswith(".certspec")]:
137 name
= certspec
.split(".certspec")[0]
138 pem
= os
.path
.join(pemfolder
, "{}.cert.pem".format(name
))
140 print("Generating public certificate {} (pem={})".format(name
, pem
))
142 with
open(os
.path
.join(root
, certspec
), "r") as certspec_file
:
143 certspec_data
= certspec_file
.read()
144 with
open(pem
, "w") as pem_file
:
146 pycert
, [], inputdata
=certspec_data
, outputstream
=pem_file
170 for keyspec
in [i
for i
in files
if i
.endswith(".keyspec")]:
171 parts
= keyspec
.split(".")
174 if key_type
not in ["ca", "client", "server"]:
176 "{}: keyspec filenames must be of the form XXX.client.keyspec "
177 "or XXX.ca.keyspec (key_type={})".format(keyspec
, key_type
)
179 key_pem
= os
.path
.join(pemfolder
, "{}.key.pem".format(name
))
181 print("Generating private key {} (pem={})".format(name
, key_pem
))
183 with
open(os
.path
.join(root
, keyspec
), "r") as keyspec_file
:
184 keyspec_data
= keyspec_file
.read()
185 with
open(key_pem
, "w") as pem_file
:
187 pykey
, [], inputdata
=keyspec_data
, outputstream
=pem_file
192 cert_pem
= os
.path
.join(pemfolder
, "{}.cert.pem".format(name
))
193 if not os
.path
.exists(cert_pem
):
195 "There has to be a corresponding certificate named {} for "
196 "the keyspec {}".format(cert_pem
, keyspec
)
199 p12
= os
.path
.join(pemfolder
, "{}.key.p12".format(name
))
201 "Converting private key {} to PKCS12 (p12={})".format(key_pem
, p12
)
217 "file:" + pwfile
.name
,
223 print("Importing private key {} to database".format(key_pem
))
226 ["-i", p12
, "-d", srcDir
, "-w", pwfile
.name
, "-k", pwfile
.name
],
233 cert_pem
, os
.path
.join(srcDir
, "{}.ca".format(name
))
235 elif key_type
== "client":
236 shutil
.copyfile(p12
, os
.path
.join(srcDir
, "{}.client".format(name
)))
237 elif key_type
== "server":
238 pass # Nothing to do for server keys
241 "State error: Unknown keyspec key_type: {}".format(key_type
)
247 build
= MozbuildObject
.from_environment()
248 certdir
= os
.path
.join(build
.topsrcdir
, "build", "pgo", "certs")
249 certificateStatus
= constructCertDatabase(build
, certdir
)
250 if certificateStatus
:
251 print("TEST-UNEXPECTED-FAIL | SSL Server Certificate generation")
252 sys
.exit(certificateStatus
)