summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/tools/ci/regen_certs.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/tools/ci/regen_certs.py')
-rw-r--r--testing/web-platform/tests/tools/ci/regen_certs.py102
1 files changed, 102 insertions, 0 deletions
diff --git a/testing/web-platform/tests/tools/ci/regen_certs.py b/testing/web-platform/tests/tools/ci/regen_certs.py
new file mode 100644
index 0000000000..8f3abdcad6
--- /dev/null
+++ b/testing/web-platform/tests/tools/ci/regen_certs.py
@@ -0,0 +1,102 @@
+# mypy: allow-untyped-defs
+
+import argparse
+import base64
+import logging
+import subprocess
+import sys
+
+
+logger = logging.getLogger(__name__)
+
+
+# TODO(Issue #24180): Regenerate SXG fingerprint too.
+CHROME_SPKI_CERTS_CONTENT = """\
+# This file is automatically generated by 'wpt regen-certs'
+# DO NOT EDIT MANUALLY.
+
+# tools/certs/web-platform.test.pem
+WPT_FINGERPRINT = '{wpt_fingerprint}'
+
+# signed-exchange/resources/127.0.0.1.sxg.pem
+SXG_WPT_FINGERPRINT = '0Rt4mT6SJXojEMHTnKnlJ/hBKMBcI4kteBlhR1eTTdk='
+
+IGNORE_CERTIFICATE_ERRORS_SPKI_LIST = [
+ WPT_FINGERPRINT,
+ SXG_WPT_FINGERPRINT
+]
+"""
+
+
+def get_parser():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--checkend-seconds", type=int, default=5184000,
+ help="The number of seconds the certificates must be valid for")
+ parser.add_argument("--force", action="store_true",
+ help="Regenerate certificates even if not reaching expiry")
+ return parser
+
+
+def check_cert(certificate, checkend_seconds):
+ """Checks whether an x509 certificate will expire within a set period.
+
+ Returns 0 if the certificate will not expire, non-zero otherwise."""
+ cmd = [
+ "openssl", "x509",
+ "-checkend", str(checkend_seconds),
+ "-noout",
+ "-in", certificate
+ ]
+ logger.info("Running '%s'" % " ".join(cmd))
+ return subprocess.call(cmd)
+
+
+def regen_certs():
+ """Regenerate the wpt openssl certificates, by delegating to wptserve."""
+ cmd = [
+ sys.executable, "wpt", "serve",
+ "--config", "tools/certs/config.json",
+ "--exit-after-start",
+ ]
+ logger.info("Running '%s'" % " ".join(cmd))
+ subprocess.check_call(cmd)
+
+
+def regen_chrome_spki():
+ """Regenerate the SPKI fingerprints for Chrome's ignore-cert list.
+
+ Chrome requires us to explicitly list which certificates are ignored by its
+ security-checking, by listing a base64 hash of the public key. This will
+ change every time we replace our certificates, so we store the hashes in a
+ file and regenerate it here.
+ """
+ wpt_spki = calculate_spki("tools/certs/web-platform.test.pem")
+ with open("tools/wptrunner/wptrunner/browsers/chrome_spki_certs.py", "w") as f:
+ f.write(CHROME_SPKI_CERTS_CONTENT.format(wpt_fingerprint=wpt_spki))
+
+
+def calculate_spki(cert_path):
+ """Calculate the SPKI fingerprint for a given x509 certificate."""
+ # We use shell=True as we control the input |cert_path|, and piping
+ # correctly across processes is non-trivial in Python.
+ cmd = (f"openssl x509 -noout -pubkey -in {cert_path} | " +
+ "openssl pkey -pubin -outform der | " +
+ "openssl dgst -sha256 -binary")
+ dgst_output = subprocess.check_output(cmd, shell=True)
+
+ return base64.b64encode(dgst_output).decode('utf-8')
+
+
+def run(**kwargs):
+ logging.basicConfig()
+
+ if kwargs["force"]:
+ logger.info("Force regenerating WPT certificates")
+ checkend_seconds = kwargs["checkend_seconds"]
+ if (kwargs["force"] or
+ check_cert("tools/certs/cacert.pem", checkend_seconds) or
+ check_cert("tools/certs/web-platform.test.pem", checkend_seconds)):
+ regen_certs()
+ regen_chrome_spki()
+ else:
+ logger.info("Certificates are still valid for at least %s seconds, skipping regeneration" % checkend_seconds)