summaryrefslogtreecommitdiffstats
path: root/build/pgo/genpgocert.py
diff options
context:
space:
mode:
Diffstat (limited to 'build/pgo/genpgocert.py')
-rw-r--r--build/pgo/genpgocert.py252
1 files changed, 252 insertions, 0 deletions
diff --git a/build/pgo/genpgocert.py b/build/pgo/genpgocert.py
new file mode 100644
index 0000000000..e3d2c4c88f
--- /dev/null
+++ b/build/pgo/genpgocert.py
@@ -0,0 +1,252 @@
+#!/usr/bin/env python
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# This script exists to generate the Certificate Authority and server
+# certificates used for SSL testing in Mochitest. The already generated
+# certs are located at $topsrcdir/build/pgo/certs/ .
+
+import os
+import random
+import re
+import shutil
+import subprocess
+import sys
+from distutils.spawn import find_executable
+
+import mozinfo
+from mozbuild.base import BinaryNotFoundException, MozbuildObject
+from mozfile import NamedTemporaryFile, TemporaryDirectory
+from mozprofile.permissions import ServerLocations
+
+dbFiles = [
+ re.compile("^cert[0-9]+\.db$"),
+ re.compile("^key[0-9]+\.db$"),
+ re.compile("^secmod\.db$"),
+]
+
+
+def unlinkDbFiles(path):
+ for root, dirs, files in os.walk(path):
+ for name in files:
+ for dbFile in dbFiles:
+ if dbFile.match(name) and os.path.exists(os.path.join(root, name)):
+ os.unlink(os.path.join(root, name))
+
+
+def dbFilesExist(path):
+ for root, dirs, files in os.walk(path):
+ for name in files:
+ for dbFile in dbFiles:
+ if dbFile.match(name) and os.path.exists(os.path.join(root, name)):
+ return True
+ return False
+
+
+def runUtil(util, args, inputdata=None, outputstream=None):
+ env = os.environ.copy()
+ if mozinfo.os == "linux":
+ pathvar = "LD_LIBRARY_PATH"
+ app_path = os.path.dirname(util)
+ if pathvar in env:
+ env[pathvar] = "%s%s%s" % (app_path, os.pathsep, env[pathvar])
+ else:
+ env[pathvar] = app_path
+ proc = subprocess.Popen(
+ [util] + args,
+ env=env,
+ stdin=subprocess.PIPE if inputdata else None,
+ stdout=outputstream,
+ universal_newlines=True,
+ )
+ proc.communicate(inputdata)
+ return proc.returncode
+
+
+def createRandomFile(randomFile):
+ for count in xrange(0, 2048):
+ randomFile.write(chr(random.randint(0, 255)))
+
+
+def writeCertspecForServerLocations(fd):
+ locations = ServerLocations(
+ os.path.join(build.topsrcdir, "build", "pgo", "server-locations.txt")
+ )
+ SAN = []
+ for loc in [
+ i for i in iter(locations) if i.scheme == "https" and "nocert" not in i.options
+ ]:
+ customCertOption = False
+ customCertRE = re.compile("^cert=(?:\w+)")
+ for _ in [i for i in loc.options if customCertRE.match(i)]:
+ customCertOption = True
+ break
+
+ if "ipV4Address" in loc.options:
+ loc.host = "ip4:" + loc.host
+
+ if not customCertOption:
+ SAN.append(loc.host)
+
+ fd.write(
+ "issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization\n" # NOQA: E501
+ )
+ fd.write("subject:{}\n".format(SAN[0]))
+ fd.write("extension:subjectAlternativeName:{}\n".format(",".join(SAN)))
+
+
+def constructCertDatabase(build, srcDir):
+ try:
+ certutil = build.get_binary_path(what="certutil")
+ pk12util = build.get_binary_path(what="pk12util")
+ except BinaryNotFoundException as e:
+ print("{}\n\n{}\n".format(e, e.help()))
+ return 1
+ openssl = find_executable("openssl")
+ pycert = os.path.join(build.topsrcdir, "security", "manager", "tools", "pycert.py")
+ pykey = os.path.join(build.topsrcdir, "security", "manager", "tools", "pykey.py")
+
+ with NamedTemporaryFile(mode="wt+") as pwfile, TemporaryDirectory() as pemfolder:
+ pwfile.write("\n")
+ pwfile.flush()
+
+ if dbFilesExist(srcDir):
+ # Make sure all DB files from src are really deleted
+ unlinkDbFiles(srcDir)
+
+ # Copy all .certspec and .keyspec files to a temporary directory
+ for root, dirs, files in os.walk(srcDir):
+ for spec in [
+ i for i in files if i.endswith(".certspec") or i.endswith(".keyspec")
+ ]:
+ shutil.copyfile(os.path.join(root, spec), os.path.join(pemfolder, spec))
+
+ # Write a certspec for the "server-locations.txt" file to that temporary directory
+ pgoserver_certspec = os.path.join(pemfolder, "pgoserver.certspec")
+ if os.path.exists(pgoserver_certspec):
+ raise Exception(
+ "{} already exists, which isn't allowed".format(pgoserver_certspec)
+ )
+ with open(pgoserver_certspec, "w") as fd:
+ writeCertspecForServerLocations(fd)
+
+ # Generate certs for all certspecs
+ for root, dirs, files in os.walk(pemfolder):
+ for certspec in [i for i in files if i.endswith(".certspec")]:
+ name = certspec.split(".certspec")[0]
+ pem = os.path.join(pemfolder, "{}.cert.pem".format(name))
+
+ print("Generating public certificate {} (pem={})".format(name, pem))
+
+ with open(os.path.join(root, certspec), "r") as certspec_file:
+ certspec_data = certspec_file.read()
+ with open(pem, "w") as pem_file:
+ status = runUtil(
+ pycert, [], inputdata=certspec_data, outputstream=pem_file
+ )
+ if status:
+ return status
+
+ status = runUtil(
+ certutil,
+ [
+ "-A",
+ "-n",
+ name,
+ "-t",
+ "P,,",
+ "-i",
+ pem,
+ "-d",
+ srcDir,
+ "-f",
+ pwfile.name,
+ ],
+ )
+ if status:
+ return status
+
+ for keyspec in [i for i in files if i.endswith(".keyspec")]:
+ parts = keyspec.split(".")
+ name = parts[0]
+ key_type = parts[1]
+ if key_type not in ["ca", "client", "server"]:
+ raise Exception(
+ "{}: keyspec filenames must be of the form XXX.client.keyspec "
+ "or XXX.ca.keyspec (key_type={})".format(keyspec, key_type)
+ )
+ key_pem = os.path.join(pemfolder, "{}.key.pem".format(name))
+
+ print("Generating private key {} (pem={})".format(name, key_pem))
+
+ with open(os.path.join(root, keyspec), "r") as keyspec_file:
+ keyspec_data = keyspec_file.read()
+ with open(key_pem, "w") as pem_file:
+ status = runUtil(
+ pykey, [], inputdata=keyspec_data, outputstream=pem_file
+ )
+ if status:
+ return status
+
+ cert_pem = os.path.join(pemfolder, "{}.cert.pem".format(name))
+ if not os.path.exists(cert_pem):
+ raise Exception(
+ "There has to be a corresponding certificate named {} for "
+ "the keyspec {}".format(cert_pem, keyspec)
+ )
+
+ p12 = os.path.join(pemfolder, "{}.key.p12".format(name))
+ print(
+ "Converting private key {} to PKCS12 (p12={})".format(key_pem, p12)
+ )
+ status = runUtil(
+ openssl,
+ [
+ "pkcs12",
+ "-export",
+ "-inkey",
+ key_pem,
+ "-in",
+ cert_pem,
+ "-name",
+ name,
+ "-out",
+ p12,
+ "-passout",
+ "file:" + pwfile.name,
+ ],
+ )
+ if status:
+ return status
+
+ print("Importing private key {} to database".format(key_pem))
+ status = runUtil(
+ pk12util,
+ ["-i", p12, "-d", srcDir, "-w", pwfile.name, "-k", pwfile.name],
+ )
+ if status:
+ return status
+
+ if key_type == "ca":
+ shutil.copyfile(
+ cert_pem, os.path.join(srcDir, "{}.ca".format(name))
+ )
+ elif key_type == "client":
+ shutil.copyfile(p12, os.path.join(srcDir, "{}.client".format(name)))
+ elif key_type == "server":
+ pass # Nothing to do for server keys
+ else:
+ raise Exception(
+ "State error: Unknown keyspec key_type: {}".format(key_type)
+ )
+
+ return 0
+
+
+build = MozbuildObject.from_environment()
+certdir = os.path.join(build.topsrcdir, "build", "pgo", "certs")
+certificateStatus = constructCertDatabase(build, certdir)
+if certificateStatus:
+ print("TEST-UNEXPECTED-FAIL | SSL Server Certificate generation")
+sys.exit(certificateStatus)