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/ .
18 from mozbuild
.base
import BinaryNotFoundException
, MozbuildObject
19 from mozfile
import NamedTemporaryFile
, TemporaryDirectory
20 from mozprofile
.permissions
import ServerLocations
23 re
.compile(r
"^cert[0-9]+\.db$"),
24 re
.compile(r
"^key[0-9]+\.db$"),
25 re
.compile(r
"^secmod\.db$"),
29 def unlinkDbFiles(path
):
30 for root
, dirs
, files
in os
.walk(path
):
32 for dbFile
in dbFiles
:
33 if dbFile
.match(name
) and os
.path
.exists(os
.path
.join(root
, name
)):
34 os
.unlink(os
.path
.join(root
, name
))
37 def dbFilesExist(path
):
38 for root
, dirs
, files
in os
.walk(path
):
40 for dbFile
in dbFiles
:
41 if dbFile
.match(name
) and os
.path
.exists(os
.path
.join(root
, name
)):
46 def runUtil(util
, args
, inputdata
=None, outputstream
=None):
47 env
= os
.environ
.copy()
48 if mozinfo
.os
== "linux":
49 pathvar
= "LD_LIBRARY_PATH"
50 app_path
= os
.path
.dirname(util
)
52 env
[pathvar
] = "%s%s%s" % (app_path
, os
.pathsep
, env
[pathvar
])
54 env
[pathvar
] = app_path
55 proc
= subprocess
.Popen(
58 stdin
=subprocess
.PIPE
if inputdata
else None,
60 universal_newlines
=True,
62 proc
.communicate(inputdata
)
63 return proc
.returncode
66 def createRandomFile(randomFile
):
67 for count
in xrange(0, 2048):
68 randomFile
.write(chr(random
.randint(0, 255)))
71 def writeCertspecForServerLocations(fd
):
72 locations
= ServerLocations(
73 os
.path
.join(build
.topsrcdir
, "build", "pgo", "server-locations.txt")
77 i
for i
in iter(locations
) if i
.scheme
== "https" and "nocert" not in i
.options
79 customCertOption
= False
80 customCertRE
= re
.compile(r
"^cert=(?:\w+)")
81 for _
in [i
for i
in loc
.options
if customCertRE
.match(i
)]:
82 customCertOption
= True
85 if "ipV4Address" in loc
.options
:
86 loc
.host
= "ip4:" + loc
.host
88 if not customCertOption
:
92 "issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization\n" # NOQA: E501
94 fd
.write("subject:{}\n".format(SAN
[0]))
95 fd
.write("extension:subjectAlternativeName:{}\n".format(",".join(SAN
)))
98 def constructCertDatabase(build
, srcDir
):
100 certutil
= build
.get_binary_path(what
="certutil")
101 pk12util
= build
.get_binary_path(what
="pk12util")
102 except BinaryNotFoundException
as e
:
103 print("{}\n\n{}\n".format(e
, e
.help()))
105 openssl
= shutil
.which("openssl")
106 pycert
= os
.path
.join(build
.topsrcdir
, "security", "manager", "tools", "pycert.py")
107 pykey
= os
.path
.join(build
.topsrcdir
, "security", "manager", "tools", "pykey.py")
109 with
NamedTemporaryFile(mode
="wt+") as pwfile
, TemporaryDirectory() as pemfolder
:
113 if dbFilesExist(srcDir
):
114 # Make sure all DB files from src are really deleted
115 unlinkDbFiles(srcDir
)
117 # Copy all .certspec and .keyspec files to a temporary directory
118 for root
, dirs
, files
in os
.walk(srcDir
):
120 i
for i
in files
if i
.endswith(".certspec") or i
.endswith(".keyspec")
122 shutil
.copyfile(os
.path
.join(root
, spec
), os
.path
.join(pemfolder
, spec
))
124 # Write a certspec for the "server-locations.txt" file to that temporary directory
125 pgoserver_certspec
= os
.path
.join(pemfolder
, "pgoserver.certspec")
126 if os
.path
.exists(pgoserver_certspec
):
128 "{} already exists, which isn't allowed".format(pgoserver_certspec
)
130 with
open(pgoserver_certspec
, "w") as fd
:
131 writeCertspecForServerLocations(fd
)
133 # Generate certs for all certspecs
134 for root
, dirs
, files
in os
.walk(pemfolder
):
135 for certspec
in [i
for i
in files
if i
.endswith(".certspec")]:
136 name
= certspec
.split(".certspec")[0]
137 pem
= os
.path
.join(pemfolder
, "{}.cert.pem".format(name
))
139 print("Generating public certificate {} (pem={})".format(name
, pem
))
141 with
open(os
.path
.join(root
, certspec
), "r") as certspec_file
:
142 certspec_data
= certspec_file
.read()
143 with
open(pem
, "w") as pem_file
:
145 pycert
, [], inputdata
=certspec_data
, outputstream
=pem_file
169 for keyspec
in [i
for i
in files
if i
.endswith(".keyspec")]:
170 parts
= keyspec
.split(".")
173 if key_type
not in ["ca", "client", "server"]:
175 "{}: keyspec filenames must be of the form XXX.client.keyspec "
176 "or XXX.ca.keyspec (key_type={})".format(keyspec
, key_type
)
178 key_pem
= os
.path
.join(pemfolder
, "{}.key.pem".format(name
))
180 print("Generating private key {} (pem={})".format(name
, key_pem
))
182 with
open(os
.path
.join(root
, keyspec
), "r") as keyspec_file
:
183 keyspec_data
= keyspec_file
.read()
184 with
open(key_pem
, "w") as pem_file
:
186 pykey
, [], inputdata
=keyspec_data
, outputstream
=pem_file
191 cert_pem
= os
.path
.join(pemfolder
, "{}.cert.pem".format(name
))
192 if not os
.path
.exists(cert_pem
):
194 "There has to be a corresponding certificate named {} for "
195 "the keyspec {}".format(cert_pem
, keyspec
)
198 p12
= os
.path
.join(pemfolder
, "{}.key.p12".format(name
))
200 "Converting private key {} to PKCS12 (p12={})".format(key_pem
, p12
)
216 "file:" + pwfile
.name
,
222 print("Importing private key {} to database".format(key_pem
))
225 ["-i", p12
, "-d", srcDir
, "-w", pwfile
.name
, "-k", pwfile
.name
],
232 cert_pem
, os
.path
.join(srcDir
, "{}.ca".format(name
))
234 elif key_type
== "client":
235 shutil
.copyfile(p12
, os
.path
.join(srcDir
, "{}.client".format(name
)))
236 elif key_type
== "server":
237 pass # Nothing to do for server keys
240 "State error: Unknown keyspec key_type: {}".format(key_type
)
246 build
= MozbuildObject
.from_environment()
247 certdir
= os
.path
.join(build
.topsrcdir
, "build", "pgo", "certs")
248 certificateStatus
= constructCertDatabase(build
, certdir
)
249 if certificateStatus
:
250 print("TEST-UNEXPECTED-FAIL | SSL Server Certificate generation")
251 sys
.exit(certificateStatus
)